3 回答
TA贡献1765条经验 获得超5个赞
请记住,除非您是手动编写程序集,否则您不是在编程到机器的内存模型,而是在编程到Go的内存模型。这意味着,即使原始赋值与您的架构是原子的,Go也需要使用原子包来确保所有支持的架构的正确性。
对互斥锁外部标志的访问只需要是安全的,而不是严格排序的,因此可以使用原子操作,而不是总是使用互斥锁获取锁。这是一种优化,使快速路径尽可能高效,允许在热路径中使用。done
sync.Once
用于 的互斥体仅用于该函数内的互斥,以确保在设置标志之前只有一个调用方能够进入该函数。该标志是使用 编写的,因为它可能与受互斥锁保护的关键部分之外同时发生。doSlow
f()
done
atomic.StoreUint32
atomic.LoadUint32
在写入的同时读取字段,甚至是原子写入,也是一场数据竞赛。仅仅因为字段是原子读取的,并不意味着您可以使用正常的赋值来写入它,因此首先检查标志并编写done
atomic.LoadUint32
atomic.StoreUint32
对 within 的直接读取是安全的,因为它受到互斥锁的并发写入保护。同时读取值是安全的,因为两者都是读取操作。done
doSlow
atomic.LoadUint32
TA贡献1818条经验 获得超3个赞
原子操作可用于同步不同 goroutine 的执行。
如果没有同步,即使 goroutine 观察到 o.done == 1,也不能保证它会观察到 的效果。f()
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 :在关键部分,由互斥体支持,安全。
- 3 回答
- 0 关注
- 122 浏览
添加回答
举报