【发布时间】:2019-03-06 14:00:18
【问题描述】:
https://msdn.microsoft.com/en-us/magazine/jj883956.aspx
考虑轮询循环模式:
private bool _flag = true; public void Run() { // Set _flag to false on another thread new Thread(() => { _flag = false; }).Start(); // Poll the _flag field until it is set to false while (_flag) ; // The loop might never terminate! }在这种情况下,.NET 4.5 JIT 编译器可能会像这样重写循环:
if (_flag) { while (true); }在单线程情况下,这 转换是完全合法的,一般来说,提升读出 循环是一个很好的优化。但是,如果设置了 _flag 在另一个线程上设置为 false,优化可能会导致挂起。
请注意,如果 _flag 字段是可变的,JIT 编译器不会 将读数提升到循环之外。 (参见“轮询循环”部分 12 月的文章可以更详细地解释这种模式。)
如果我锁定_flag,JIT 编译器是否仍会优化代码,还是只会让它volatile 停止优化?
Eric Lippert 对 volatile 有以下看法:
坦率地说,我不鼓励你创建一个不稳定的字段。易挥发的 字段表明你正在做一些非常疯狂的事情:你是 试图在两个不同的线程上读取和写入相同的值 没有把锁到位。锁保证内存读取或 修改里面的锁观察是一致的,锁保证 一次只有一个线程访问给定的内存块,并且 很快。锁太慢的情况非常多 小,而且你会弄错代码的可能性 因为你不了解确切的内存模型非常大。一世 不要尝试编写任何低锁代码,除了最琐碎的代码 互锁操作的用法。我将“易失性”的用法留给 真正的专家。
总结一下:谁保证上面提到的优化不会破坏我的代码?只有volatile?还有lock 声明?还是别的什么?
由于 Eric Lippert 不鼓励您使用 volatile,肯定还有其他原因?
投反对票的人:我感谢对这个问题的每一个反馈。特别是如果你不赞成,我想听听你为什么认为这是一个糟糕的问题。
bool 变量不是线程同步原语:该问题是一般问题。编译器什么时候不做优化?
重复:这个问题明确与优化有关。您链接的那个没有提到优化。
【问题讨论】:
-
@Mgetz 嘿,感谢您的评论。我既不是专门寻找联锁操作也不是取消令牌。我只想知道在这种特定情况下如何停止上述优化。用锁包裹字段会起作用吗?还有什么?优化后如何防止代码中断/防止优化?
-
互锁操作是一种特殊情况,它告诉编译器非常特殊地对待它们。您实际上是在告诉编译器您需要它来处理该内存位置,就好像它可能随时更改一样。作为回报,您只会使用特殊的访问方式(互锁)。然后它可以适当地围绕它进行优化
-
bool 变量不是线程同步原语,永远不会。当您正确执行此操作时,您永远不必担心 Microsoft 弄错 volatile 关键字的确切方式。 ManualResetEventSlim 是 Interlocked 的一个很好的包装器,您可以通过在 if 语句中使用 Volatile.Read() 和 Volatile.Write() 来设置它来使 bool 工作。只要不忽略异常,Task 和 CancellationToken 就可以提高抽象级别,几乎没有缺点。
-
@Mgetz 这个问题明确与优化有关。您链接的那个没有提到优化。
-
@Mgetz 是的,谢谢。我现在明白了。这就是为什么我在你的句子中附加了“当场没有互锁时”。如果有人将其发布为答案,我会接受。更多细节总是好的。
标签: c# .net multithreading volatile jit