这个问题是 不是 关于种族条件,原子,或为什么你应该在你的代码中使用的锁。我已经知道这些了。
更新:我的问题不是“存在易失性内存的怪异现象”(我知道吗),我的问题是“ .NET运行时抽象没有那么远,所以您永远不会看到它”。
请参阅http://www.yoda.arachsys.com/csharp/threads/volatility.shtml, 以及有关字符串属性本身是否线程安全的第一个答案?
(它们实际上是同一篇文章,因为一个引用另一个。)一个线程设置一个布尔值,另一个线程永远循环读取该布尔值- 那些文章声称读取线程可能会缓存旧值而从不读取新值,因此因此,您需要一个锁(或使用volatile关键字)。他们声称以下代码将永远循环下去。现在,我同意锁定变量是一种很好的做法,但是我不敢相信.NET运行时会真正忽略本文所述的内存值更改。我了解他们关于易失性存储器与非易失性存储器的讨论,并且我同意他们在 非托管 存储中有一个有效的论点 __代码,但我无法相信.NET运行时不会正确地将其抽象化,因此以下代码可以实现您所期望的。 该文章甚至承认该代码将“几乎可以肯定地”工作(尽管不能保证),因此我在声明中称呼BS。任何人都可以验证以下代码是否始终有效吗?有谁能在失败的情况下获得甚至一个案例(也许您不能总是复制它)?
class BackgroundTaskDemo { private bool stopping = false; static void Main() { BackgroundTaskDemo demo = new BackgroundTaskDemo(); new Thread(demo.DoWork).Start(); Thread.Sleep(5000); demo.stopping = true; } static void DoWork() { while (!stopping) { // Do something here } } }
关键是:它可能会起作用,但不能 保证 该规范能起作用。人们通常所追求的是能够以正确的理由工作的代码,而不是通过编译器,运行时和JIT的偶然组合来工作,这些代码 可能会 在框架版本,物理CPU,平台以及x86与x64。
了解内存模型是一个非常非常复杂的领域,我并不声称自己是专家。但是在这方面 是 真正专家的人向我保证,您所看到的行为无法得到保证。
您可以根据需要发布任意数量的工作示例,但是不幸的是,除了“它通常可以工作”之外,它并没有得到太多证明。当然不能证明它可以 保证 正常工作。只需反驳一个例子,但发现这就是问题所在…
不,我没有手。
使用可重复的反例进行更新:
using System.Threading; using System; static class BackgroundTaskDemo { // make this volatile to fix it private static bool stopping = false; static void Main() { new Thread(DoWork).Start(); Thread.Sleep(5000); stopping = true; Console.WriteLine("Main exit"); Console.ReadLine(); } static void DoWork() { int i = 0; while (!stopping) { i++; } Console.WriteLine("DoWork exit " + i); } }
输出:
Main exit
但仍在全CPU下运行;请注意,此时stopping已设置为true。的ReadLine是,这样的过程不会终止。优化似乎取决于循环内代码的大小(因此i++)。显然,它仅在“发布”模式下有效。添加volatile,一切正常。
stopping
true
ReadLine
i++
volatile