C#线程真的可以caching一个值,并忽略其他线程上的值的变化吗?

这个问题不是关于种族条件,primefaces性,或为什么你应该在你的代码中使用锁。 我已经知道这些。

更新:我的问题不是“是否存在易失性内存的古怪”(我知道它的确如此),我的问题是“.NET运行时不会抽象出来,所以你永远不会看到它”。

请参阅http://www.yoda.arachsys.com/csharp/threads/volatility.shtml和第一个答案是string属性本身threadsafe?

(他们真的是相同的文章,因为一个引用另一个。)一个线程设置一个布尔和另一个线程永远循环阅读该布尔 – 这些文章声称阅读线程可能会caching旧值,从来没有读取新值,所以因此您需要locking(或使用volatile关键字)。 他们声称以下代码可能会永久循环。 现在我同意locking你的variables是一个很好的习惯,但是我不能相信.NET运行库会真正忽略一个随着文章的声明而改变的内存值。 我理解他们对易失性存储器和非易失性存储器的讨论,我同意他们在非托pipe代码中有一个有效的观点,但我不能相信.NET运行时将不会正确地抽象出来,以便下面的代码你期望什么 文章甚至承认代码将“几乎肯定”的工作(虽然不能保证),所以我打电话给BS的索赔。 任何人都可以validation它是真的下面的代码不会总是工作? 任何人都可以得到一个案例(也许你不能总是重现),哪里有失败?

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, 64。

了解记忆模型是一个非常复杂的领域,我不认为是专家; 但在这方面真正的专家的人向我保证,你所看到的行为是不能保证的。

你可以发布尽可能多的工作示例,但不幸的是,除了“通常有效”之外,并不能certificate这一点。 这当然不能certificate它是保证工作。 它只会采取一个反例来反驳,但发现它是问题…

不,我没有一个人。


更新可重复的反例:

 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已经被设置为trueReadLine是这样的过程不会终止。 优化似乎是依赖于循环内的代码的大小(因此i++ )。 它只能在“释放”模式下工作。 添加volatile ,它一切正常。

此示例包含本机x86代码作为注释来演示控制variables('stopLooping')是否被caching。

将“stopLooping”更改为volatile以“修复”它。

这是使用Visual Studio 2008作为发布版本构build的,并且无需debugging即可运行。

  using System; using System.Threading; /* A simple console application which demonstrates the need for the volatile keyword and shows the native x86 (JITed) code.*/ static class LoopForeverIfWeLoopOnce { private static bool stopLooping = false; static void Main() { new Thread(Loop).Start(); Thread.Sleep(1000); stopLooping = true; Console.Write("Main() is waiting for Enter to be pressed..."); Console.ReadLine(); Console.WriteLine("Main() is returning."); } static void Loop() { /* * Stack frame setup (Native x86 code): * 00000000 push ebp * 00000001 mov ebp,esp * 00000003 push edi * 00000004 push esi */ int i = 0; /* * Initialize 'i' to zero ('i' is in register edi) * 00000005 xor edi,edi */ while (!stopLooping) /* * Load 'stopLooping' into eax, test and skip loop if != 0 * 00000007 movzx eax,byte ptr ds:[001E2FE0h] * 0000000e test eax,eax * 00000010 jne 00000017 */ { i++; /* * Increment 'i' * 00000012 inc edi */ /* * Test the cached value of 'stopped' still in * register eax and do it again if it's still * zero (false), which it is if we get here: * 00000013 test eax,eax * 00000015 je 00000012 */ } Console.WriteLine("i={0}", i); } } 

FWIW:

  • 已经从MS C ++编译器(非托pipe代码)中看到了这种编译器优化。
  • 我不知道这是否发生在C#
  • debugging时不会发生(debugging时编译器优化会自动禁用)
  • 即使这样的优化现在不会发生,你也打赌他们永远不会在未来版本的JIT编译器中引入这种优化。