interview-go icon indicating copy to clipboard operation
interview-go copied to clipboard

q015 “双检查实现单例”问题

Open mlixytz opened this issue 3 years ago • 5 comments

答案是:在多核CPU中,因为CPU缓存会导致多个核心中变量值不同步。 我的观点是:当前核心的变量值发生改变时,其他核心缓存中的值会置为不可用,当读取或修改时会重新从主存里读取。所以应该不会有不同步的问题。并且我也无法浮现这个问题 求解

mlixytz avatar Oct 13 '20 06:10 mlixytz

大部分语言都存在多核CPU缓存不同步问题,包括Java的volatile和C#的volatile关键字 都是做多线程在多核CPU强制同步的。 如果没有用这个关键字,在多线程环境下,一个正在执行的线程中的变量被其他变量修改是无法同步的。

lifei6671 avatar Oct 15 '20 07:10 lifei6671

@lifei6671 谢谢解答。我现在明白了。我之前认为cpu多核之间的缓存一致性协议(比如MESI协议)会同步多核缓存,不会出现多核cache中变量值不同步的情况。 现在了解到,cpu不是直接将修改写到缓存里的,而是要经过Store buffer。这样就会产生多核cache间的同步延迟,导致多核cpu缓存中的变量不同步。

mlixytz avatar Oct 16 '20 06:10 mlixytz

大部分语言都存在多核CPU缓存不同步问题,包括Java的volatile和C#的volatile关键字 都是做多线程在多核CPU强制同步的。 如果没有用这个关键字,在多线程环境下,一个正在执行的线程中的变量被其他变量修改是无法同步的。

我还是没太理解,这个问题感觉应该是能实现f()只执行一次才对,只不过实现很差,因为后续

if o.done == 0 {
		o.done = 1

这个操作是被锁保护的,也就是说,其他线程会等在锁外面,并且正确的被 o.done == 0 拦截掉 这里的实现方法的问题应该是

  1. 有大量routine等在o.m.Lock()这句话上面,尽管后续 o.done == 0 能把他们否决出去
  2. 因为在f()之前把 done设置为 1 了,可能导致后续routine在 f() 执行完之前就被放过去,导致初始化未完成,这种情况下有可能出现 panic

所以2的问题官方是这么做的

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

NightmareZero avatar Dec 07 '21 09:12 NightmareZero

这个和Java等语言出现volatile关键字没有什么联系,volatile关键字是和golang的atomic包功能一样,可以让在没有使用同步锁的时候也能并发给变量赋值和读取。但是题目里已经使用了同步锁,所以不会有问题,这个仓库没有更新,可以看下题目的原出处,在评论区作者已经讨论很清楚了原文

hayden-pan avatar Aug 09 '22 14:08 hayden-pan

大部分语言都存在多核CPU缓存不同步问题,包括Java的volatile和C#的volatile关键字 都是做多线程在多核CPU强制同步的。 如果没有用这个关键字,在多线程环境下,一个正在执行的线程中的变量被其他变量修改是无法同步的。

我还是没太理解,这个问题感觉应该是能实现f()只执行一次才对,只不过实现很差,因为后续

if o.done == 0 {
		o.done = 1

这个操作是被锁保护的,也就是说,其他线程会等在锁外面,并且正确的被 o.done == 0 拦截掉 这里的实现方法的问题应该是

  1. 有大量routine等在o.m.Lock()这句话上面,尽管后续 o.done == 0 能把他们否决出去
  2. 因为在f()之前把 done设置为 1 了,可能导致后续routine在 f() 执行完之前就被放过去,导致初始化未完成,这种情况下有可能出现 panic

所以2的问题官方是这么做的

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

你的理解没有问题,我在题目作者博客看到了最后的讨论原文

hayden-pan avatar Aug 09 '22 14:08 hayden-pan