为什么是原子的。StoreUint32 优先于同步中的正常分配。一次?

在阅读 Go 的源代码时,我对 src/sync/once.go 中的代码有疑问:


func (o *Once) Do(f func()) {

    // Note: Here is an incorrect implementation of Do:

    //

    //  if atomic.CompareAndSwapUint32(&o.done, 0, 1) {

    //      f()

    //  }

    //

    // Do guarantees that when it returns, f has finished.

    // This implementation would not implement that guarantee:

    // given two simultaneous calls, the winner of the cas would

    // call f, and the second would return immediately, without

    // waiting for the first's call to f to complete.

    // This is why the slow path falls back to a mutex, and why

    // the atomic.StoreUint32 must be delayed until after f returns.


    if atomic.LoadUint32(&o.done) == 0 {

        // Outlined slow-path to allow inlining of the fast-path.

        o.doSlow(f)

    }

}


func (o *Once) doSlow(f func()) {

    o.m.Lock()

    defer o.m.Unlock()

    if o.done == 0 {

        defer atomic.StoreUint32(&o.done, 1)

        f()

    }

}

为什么使用,而不是说?这些不是等价的吗?有什么区别?atomic.StoreUint32o.done = 1


我们是否必须使用原子运算()来确保其他goroutines可以在内存较弱的机器上观察到之前设置为1的效果?atomic.StoreUint32f()o.done


繁华开满天机
浏览 123回答 3
3回答

POPMUISE

请记住,除非您是手动编写程序集,否则您不是在编程到机器的内存模型,而是在编程到Go的内存模型。这意味着,即使原始赋值与您的架构是原子的,Go也需要使用原子包来确保所有支持的架构的正确性。对互斥锁外部标志的访问只需要是安全的,而不是严格排序的,因此可以使用原子操作,而不是总是使用互斥锁获取锁。这是一种优化,使快速路径尽可能高效,允许在热路径中使用。donesync.Once用于 的互斥体仅用于该函数内的互斥,以确保在设置标志之前只有一个调用方能够进入该函数。该标志是使用 编写的,因为它可能与受互斥锁保护的关键部分之外同时发生。doSlowf()doneatomic.StoreUint32atomic.LoadUint32在写入的同时读取字段,甚至是原子写入,也是一场数据竞赛。仅仅因为字段是原子读取的,并不意味着您可以使用正常的赋值来写入它,因此首先检查标志并编写doneatomic.LoadUint32atomic.StoreUint32对 within 的直接读取是安全的,因为它受到互斥锁的并发写入保护。同时读取值是安全的,因为两者都是读取操作。donedoSlowatomic.LoadUint32

泛舟湖上清波郎朗

原子操作可用于同步不同 goroutine 的执行。如果没有同步,即使 goroutine 观察到 o.done == 1,也不能保证它会观察到 的效果。f()

慕标5832272

func (o *Once) Do(f func()) {    if atomic.LoadUint32(&o.done) == 0 {       # 1        // Outlined slow-path to allow inlining of the fast-path.        o.doSlow(f)    }}func (o *Once) doSlow(f func()) {    o.m.Lock()    defer o.m.Unlock()    if o.done == 0 {                            # 2        defer atomic.StoreUint32(&o.done, 1)    # 3        f()    }}#1和#3:#1是读的,#3是写的,它不安全,需要mutext来保护#2和#3 :在关键部分,由互斥体支持,安全。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go