【问题标题】:Does LeaveCriticalSection() flushes the cached variables to memory?LeaveCriticalSection() 是否将缓存的变量刷新到内存?
【发布时间】:2017-07-29 20:53:51
【问题描述】:

假设我有以下代码:

// global variable
int i = 0;

...

EnterCriticalSection(&CriticalSection);
i = 45;
i = 24;
i = 32;
LeaveCriticalSection(&CriticalSection);

CPU能否缓存变量i,当i被修改时,缓存的副本就是修改的那个,而当LeaveCriticalSection()被调用时,i的缓存值被刷新到内存中(所以例如其他线程可以看到更新的值)?

【问题讨论】:

  • 它可以做任何事情,只要程序的可观察行为符合 C 标准为代码指定的行为
  • @HarryJohnston 最新的 C 标准确实涵盖了多线程。 OP 的编译器是否符合标准是另一回事,但标准总是在设计时考虑到现有的实现
  • @HarryJohnston 是的,我确定。编译器知道 CPU 做了什么并相应地生成程序集
  • @Christopher,实际上,LeaveCriticalSection 引入了内存屏障,因此所有缓存的写入将在返回之前刷新到内存。这种行为没有记录在案,但如果要改变它,地球上几乎所有的多线程应用程序都会崩溃。
  • @Olaf,当然编译器会重新排序语句,但是如果它在调用 API 函数时重新排序内存操作,它最好知道该 API 函数实际做了什么,以及它是否重新排序跨 API 函数的内存操作,进入或离开关键部分,好吧,IMO 这只是病态的。 (如果您更仔细地阅读我所说的内容,您会注意到我将 Visual Studio 归类为“较旧的 C 编译器”,与 C11 编译器不同。是 MM 提出了 C11,我只是对此作出回应。 )

标签: c multithreading winapi


【解决方案1】:

根据this page from MSDN

以下同步函数使用适当的屏障来确保内存排序:

  • 进入或离开临界区的函数。

这并不一定意味着数据被“刷新到内存”。实际上,您可能会认为某些缓存是内存的一部分,而其他缓存可能与单个 CPU 相关联。 “适当的屏障”为机器架构做任何必要的事情,以保证所有 CPU 具有一致的视图。

要了解更多信息,您可能需要从memory barriersmemory ordering 上的维基百科页面开始。

【讨论】:

  • 引用部分并不意味着刷新到任何类型的内存。 x86 上的指令是什么?
  • 只要其他线程获取相同的cs,就不会观察到除了i=32以外的任何东西。最重要的是,编译器将折叠分配并且只保留=32。如果其他线程没有获得CS,那就是UB。使用 std::atomic,它巧妙地解决了所有问题。
  • @voo:这是一个很好的观点,我尝试将答案编辑得更精确一些,并使用一些链接而不是包含一篇文章。欢迎提出任何具体建议。
  • @sergei:是的,因为变量不是易失性的,编译器优化可能会省略存储。我想提一下,但似乎不相关。至于std::atomic,问题标记为c
  • @rici 好多了,重要的是要理解“刷新到内存”只是实现所需部分的一种可能(非常糟糕)的策略。现在很清楚,删除了我的反对票。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-06-30
  • 2014-04-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-08
  • 2012-07-02
相关资源
最近更新 更多