我理解一个线程可以缓存一个值ignore changes made on another thread,但我想知道这个变种。线程是否有可能更改缓存值,然后永远不会离开其缓存,因此其他线程永远不可见?
例如,此代码是否可以打印“Flag is true”,因为线程a
从未看到线程b
对flag
所做的更改? (我不能这样做,但我不能证明它,或者它的一些变化,不会。)
var flag = true;
var a = new Thread(() => {
Thread.Sleep(200);
Console.WriteLine($"Flag is {flag}");
});
var b = new Thread(() => {
flag = false;
while (true) {
// Do something to avoid a memory barrier
}
});
a.Start();
b.Start();
a.Join();
我可以想象,在线程b
flag
上可以缓存在CPU寄存器中,然后将其设置为false
,当b
进入{{1}时循环永远不会有机会(或从不关心)将while
的值写回内存,因此flag
始终将a
视为真。
从this answer中列出的内存屏障生成器来看,这对我来说似乎是可能的。我对么?我无法在实践中证明这一点。任何人都可以提出一个例子吗?
答案 0 :(得分:1)
线程是否有可能更改缓存值,然后永远不会离开其缓存,因此其他线程永远不可见?
如果我们正在谈论硬件缓存,那么我们需要讨论特定的处理器系列。如果你在x86(和x64)上工作(似乎很可能),你需要知道那些处理器实际上具有比.NET所需的更强大的内存模型。在x86系统中,缓存保持一致性,因此其他处理器不能忽略任何写入。
如果我们谈论的是优化,其中特定的存储器位置已被读入处理器寄存器,然后后续从存储器读取只是重用寄存器,那么就没有类似的模拟在写方面。您会注意到,在我们假设没有其他任何内容正在改变该内存位置之前,始终至少有一个从实际内存位置读取,因此我们可以重用该寄存器。
在写入方面,我们被告知要将某些内容推送到特定的内存位置。我们必须至少推送到该位置一次,并且在单独的寄存器中始终将先前已知的值存储在该位置(特别是如果我们的线程从不从它读取)可能是一种去优化,以便能够执行比较并且忽略了写操作。
答案 1 :(得分:0)
我不完全确定这会回答你的问题,但现在就去了。
如果您运行以下代码的发行版(非调试版),它将永远不会终止,因为永远不会看到
waitForFlag()
的更改版本。
但是,如果您注释掉任何指定的行,程序将终止。
看起来在flag
循环中调用外部库会导致优化器不缓存while (flag)
的值。
同样(当然)使flag
不稳定会阻止这种优化。
flag
答案 2 :(得分:0)
研究Thread.MemoryBarrier,你将是金色的。
答案 3 :(得分:0)
从最简单到正确到最容易搞定
<select name="city">
<option value="Los Angeles">Los Angeles</option>
// Other California cities
</select>
类