q015 “双检查实现单例”问题
mlixytz opened this issue · comments
答案是:在多核CPU中,因为CPU缓存会导致多个核心中变量值不同步。
我的观点是:当前核心的变量值发生改变时,其他核心缓存中的值会置为不可用,当读取或修改时会重新从主存里读取。所以应该不会有不同步的问题。并且我也无法浮现这个问题 求解
大部分语言都存在多核CPU缓存不同步问题,包括Java的volatile
和C#的volatile
关键字 都是做多线程在多核CPU强制同步的。
如果没有用这个关键字,在多线程环境下,一个正在执行的线程中的变量被其他变量修改是无法同步的。
@lifei6671 谢谢解答。我现在明白了。我之前认为cpu多核之间的缓存一致性协议(比如MESI协议)会同步多核缓存,不会出现多核cache中变量值不同步的情况。 现在了解到,cpu不是直接将修改写到缓存里的,而是要经过Store buffer。这样就会产生多核cache间的同步延迟,导致多核cpu缓存中的变量不同步。
大部分语言都存在多核CPU缓存不同步问题,包括Java的
volatile
和C#的volatile
关键字 都是做多线程在多核CPU强制同步的。 如果没有用这个关键字,在多线程环境下,一个正在执行的线程中的变量被其他变量修改是无法同步的。
我还是没太理解,这个问题感觉应该是能实现f()只执行一次才对,只不过实现很差,因为后续
if o.done == 0 {
o.done = 1
这个操作是被锁保护的,也就是说,其他线程会等在锁外面,并且正确的被 o.done == 0 拦截掉
这里的实现方法的问题应该是
- 有大量routine等在
o.m.Lock()
这句话上面,尽管后续o.done == 0
能把他们否决出去 - 因为在f()之前把 done设置为 1 了,可能导致后续routine在 f() 执行完之前就被放过去,导致初始化未完成,这种情况下有可能出现 panic
所以2的问题官方是这么做的
defer atomic.StoreUint32(&o.done, 1)
f()
这个和Java等语言出现volatile关键字没有什么联系,volatile关键字是和golang的atomic包功能一样,可以让在没有使用同步锁的时候也能并发给变量赋值和读取。但是题目里已经使用了同步锁,所以不会有问题,这个仓库没有更新,可以看下题目的原出处,在评论区作者已经讨论很清楚了原文
大部分语言都存在多核CPU缓存不同步问题,包括Java的
volatile
和C#的volatile
关键字 都是做多线程在多核CPU强制同步的。 如果没有用这个关键字,在多线程环境下,一个正在执行的线程中的变量被其他变量修改是无法同步的。我还是没太理解,这个问题感觉应该是能实现f()只执行一次才对,只不过实现很差,因为后续
if o.done == 0 { o.done = 1这个操作是被锁保护的,也就是说,其他线程会等在锁外面,并且正确的被 o.done == 0 拦截掉 这里的实现方法的问题应该是
- 有大量routine等在
o.m.Lock()
这句话上面,尽管后续o.done == 0
能把他们否决出去- 因为在f()之前把 done设置为 1 了,可能导致后续routine在 f() 执行完之前就被放过去,导致初始化未完成,这种情况下有可能出现 panic
所以2的问题官方是这么做的
defer atomic.StoreUint32(&o.done, 1) f()
你的理解没有问题,我在题目作者博客看到了最后的讨论原文