https://msdn.microsoft.com/en-us/magazine/jj883956.aspx
Consider the polling loop pattern:
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! }
In this case, the .NET 4.5 JIT compiler might rewrite the loop like this:
if (_flag) { while (true); }
In the single-threaded case, this transformation is entirely legal and, in general, hoisting a read out of a loop is an excellent optimization. However, if the _flag is set to false on another thread, the optimization can cause a hang.
Note that if the _flag field were volatile, the JIT compiler would not hoist the read out of the loop. (See the “Polling Loop” section in the December article for a more detailed explanation of this pattern.)
如果我锁定 _flag
,JIT 编译器是否仍会像上面显示的那样优化代码,或者只会使其 volatile
停止优化?
Eric Lippert 对 volatile 的评价如下:
Frankly, I discourage you from ever making a volatile field. Volatile fields are a sign that you are doing something downright crazy: you're attempting to read and write the same value on two different threads without putting a lock in place. Locks guarantee that memory read or modified inside the lock is observed to be consistent, locks guarantee that only one thread accesses a given chunk of memory at a time, and so on. The number of situations in which a lock is too slow is very small, and the probability that you are going to get the code wrong because you don't understand the exact memory model is very large. I don't attempt to write any low-lock code except for the most trivial usages of Interlocked operations. I leave the usage of "volatile" to real experts.
总结一下:谁保证上面提到的优化不会破坏我的代码? 只有 volatile
?还有 lock
声明?还是别的?
既然 Eric Lippert 不鼓励您使用 volatile
,那么一定还有其他东西吗?
反对者:我感谢对这个问题的每一个反馈。特别是如果你不赞成它,我想听听你为什么认为这是一个糟糕的问题。
bool 变量不是线程同步原语:这个问题是一个一般性问题。什么时候编译器不做优化?
Dupilcate:这个问题明确是关于优化的。您链接的那个没有提到优化。
最佳答案
让我们回答被问到的问题:
Will the JIT compiler still optimize the code as shown above if I lock _flag or will only making it volatile stop the optimization?
好吧,我们不回答被问到的问题,因为那个问题太复杂了。让我们把它分解成一系列不太复杂的问题。
Will the JIT compiler still optimize the code as shown above if I lock _flag?
简短回答:lock
比 volatile
提供了更强的保证,所以不,不允许抖动解除读取如果在 _flag
的读取周围有锁,则循环。当然锁也必须围绕着写。锁只有在您随处使用时才有效。
private bool _flag = true;
private object _flagLock = new object();
public void Run()
{
new Thread(() => { lock(_flaglock) _flag = false; }).Start();
while (true)
lock (_flaglock)
if (!_flag)
break;
}
(当然,我注意到这是一种等待一个线程向另一个线程发出信号的非常糟糕的方式。永远不要坐在一个紧密的循环中轮询标志!使用等待句柄像一个明智的人。)
You said locks were stronger than volatiles; what does that mean?
读取 volatiles 会阻止某些操作及时移动。写入 volatiles 会阻止某些操作及时移动。锁可以防止更多 操作被及时移动。这些预防语义称为“内存栅栏”——基本上,volatiles 引入了半栅栏,锁引入了全栅栏。
有关详细信息,请阅读有关特殊副作用的 C# 规范部分。
一如既往,我会提醒您, volatile 不能为您提供全局新鲜度保证。在多线程 C# 编程中没有变量的“最新”值这样的东西,因此 volatile 读取不会为您提供“最新”值,因为它不存在。存在“最新”值的想法意味着始终观察到读取和写入在时间上具有全局一致的顺序,这是错误的。线程仍然可以不同意 volatile 读取和写入的顺序。
Locks prevent this optimization; volatiles prevent this optimization. Are those the only thing which prevents the optimization?
没有。您还可以使用 Interlocked
操作,或者您可以显式引入内存栅栏。
Do I understand enough of this to use
volatile
correctly?
没有。
What should I do?
首先不要编写多线程程序。一个程序中的多个控制线程不是一个好主意。
如果必须,不要跨线程共享内存。将线程用作低成本进程,并且仅在您有可以执行 CPU 密集型任务的空闲 CPU 时才使用它们。对所有 I/O 操作使用单线程异步。
如果您必须跨线程共享内存,请使用可用的最高级别编程结构,而不是最低级别。使用 CancellationToken
表示在异步工作流的其他地方取消的操作。
关于c# - .NET JIT 编译器可变优化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52591453/