Do函数里面 刚开始进来的时候,直接用0.done == 0 判断也能达到效果,atomic.LoadUint32(&o.done) == 0目的是啥,为了很多goroutine同时调用Do函数,减少o.m.Lock()的调用吗
有疑问加站长微信联系(非本文作者)

Do函数里面 刚开始进来的时候,直接用0.done == 0 判断也能达到效果,atomic.LoadUint32(&o.done) == 0目的是啥,为了很多goroutine同时调用Do函数,减少o.m.Lock()的调用吗
有疑问加站长微信联系(非本文作者)
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
`单行代码`
线程安全
@kingJ 线程安全是指啥,两个goroutine同时读一个int应该不会panic
你的疑问也正是我的疑问,
atomic.StoreUint32(&o.done, 1) 和 atomic.LoadUint32(&o.done) 应是保证数据一致性的
但是程序逻辑性其实不需要原子操作,因为有锁
最开始读o.done的时候并不在锁中,看看别人的文章 https://zhuanlan.zhihu.com/p/520607098
注释里面有说明, 为什么有了原子操作还加锁
疑问不在于为什么有了原子操作还要加锁,而是有了加锁还要用原子操作, 如下写法会有线程安全问题吗?
还有一个疑问就是既然用了
atomic
为何又没贯彻atomic
,doSlow
锁里面为何用if o.done == 0
而不是if atomic.LoadUint32(&o.done) == 0
可能会出现unexpected result
如果是多goroutine 情况下, o.done 和 atomic.LoadUint32(&o.done) 区别:
1、o.done的读取在执行上可能需要好几次 cpu 指令操作,这几次 cpu 指令执行中,可能有别的 goroutine 在修改这个 done 的值,导致你读取到的可能是一个不确定的值 2、用 atomic 这种原子操作,cpu 指令在执行中会保证别的goroutine不去修改这个值,所以读取到的是一个确定的值;
建议看一下 go 的内存模型: https://go.dev/ref/mem
那是原子性操作,也是线程安全的,理论上原子性操作会比锁的性能高一些
1
感谢解答,其实道理我能理解,但是就代码论代码,如果 sync.Once 写成如下这样,会有线程安全问题吗?如果有,会哪些地方有线程安全问题。
感觉没毛病
以前的大佬给出的解释,而且看官方注释也有提到内存模型问题:https://studygolang.com/articles/25299
感谢,这个答案基本上回答了我所有的疑问
谢谢回答。我也知道能确定,但是问题是不用原子对逻辑正确性没有影响,因为有锁保证。benchmark的话会发现性能也没区别。最终只能想是为了多操作系统不同底层代码和不同go版本的适配性。