为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
繁华开满天机 2022-08-01 18:48:35
在阅读 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
查看完整描述

3 回答

?
POPMUISE

TA贡献1765条经验 获得超5个赞

请记住,除非您是手动编写程序集,否则您不是在编程到机器的内存模型,而是在编程到Go的内存模型。这意味着,即使原始赋值与您的架构是原子的,Go也需要使用原子包来确保所有支持的架构的正确性。

对互斥锁外部标志的访问只需要是安全的,而不是严格排序的,因此可以使用原子操作,而不是总是使用互斥锁获取锁。这是一种优化,使快速路径尽可能高效,允许在热路径中使用。donesync.Once

用于 的互斥体仅用于该函数内的互斥,以确保在设置标志之前只有一个调用方能够进入该函数。该标志是使用 编写的,因为它可能与受互斥锁保护的关键部分之外同时发生。doSlowf()doneatomic.StoreUint32atomic.LoadUint32

在写入的同时读取字段,甚至是原子写入,也是一场数据竞赛。仅仅因为字段是原子读取的,并不意味着您可以使用正常的赋值来写入它,因此首先检查标志并编写doneatomic.LoadUint32atomic.StoreUint32

对 within 的直接读取安全的,因为它受到互斥锁的并发写入保护。同时读取值是安全的,因为两者都是读取操作。donedoSlowatomic.LoadUint32


查看完整回答
反对 回复 2022-08-01
?
泛舟湖上清波郎朗

TA贡献1818条经验 获得超3个赞

原子操作可用于同步不同 goroutine 的执行。

如果没有同步,即使 goroutine 观察到 o.done == 1,也不能保证它会观察到 的效果。f()


查看完整回答
反对 回复 2022-08-01
?
慕标5832272

TA贡献1966条经验 获得超4个赞

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 :在关键部分,由互斥体支持,安全。


查看完整回答
反对 回复 2022-08-01
  • 3 回答
  • 0 关注
  • 122 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信