Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
mlixytz opened this issue Oct 13, 2020 · 5 comments
Open

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

mlixytz opened this issue Oct 13, 2020 · 5 comments

Comments

@mlixytz
Copy link

mlixytz commented Oct 13, 2020

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

@lifei6671
Copy link
Owner

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

@mlixytz
Copy link
Author

mlixytz commented Oct 16, 2020

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

@NightmareZero
Copy link

大部分语言都存在多核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
Copy link

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

@hayden-pan
Copy link

大部分语言都存在多核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()

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants