我们真的需要在C#中使用VOLATILE关键字吗?

这是我在工作站上尝试的代码。

class Program { public static volatile bool status = true; public static void Main() { Thread FirstStart = new Thread(threadrun); FirstStart.Start(); Thread.Sleep(200); Thread thirdstart = new Thread(threadrun2); thirdstart.Start(); Console.ReadLine(); } static void threadrun() { while (status) { Console.WriteLine("Waiting.."); } } static void threadrun2() { status = false; Console.WriteLine("the bool value is now made FALSE"); } } 

如你所见,我在Main发了三个线程。 然后使用断点我跟踪线程。 我的初始概念是所有三个线程将同时被触发,但我的断点流程显示线程执行流程一个接一个地跟随(输出格式也是如此,即从上到下执行线程)。 伙计们为什么会这样?

另外,我尝试在声明中不使用volatile关键字运行相同的程序,我发现程序执行没有变化。 我怀疑volatile关键字没有实际的实际用途。 我在某个地方出错了吗?

好的,我会尝试尽可能简短地解释一个很长的故事:

编号1 :尝试使用调试器检查线程的行为与重复运行multithreading程序一样有用,并得出结论它工作正常,因为在100次测试中没有失败:错误! 线程表现为完全不确定(有些人会说是随机的)方式,你需要不同的方法来确保这样的程序能够正确运行。

编号2 :一旦删除它然后在调试模式下运行程序然后切换到发布模式, volatile的使用将变得清晰。 我想你会有一个惊喜……发布模式中发生的是编译器将优化代码(这包括重新排序指令和值的缓存)。 现在,如果您的两个线程在不同的处理器核心上运行,那么执行正在检查status值的线程的核心将缓存其值而不是反复检查它。 另一个线程将设置它,但第一个将永远不会看到更改:死锁! volatile可以防止这种情况发生。

从某种意义上说,如果代码实际上(并且很可能不会)按照您认为的那样在multithreading场景中运行,则volatile是一种保护。

你的思维方法存在缺陷。

线程相关问题的本质是它们是非确定性的。 这意味着您所观察到的可能不会指示将来会发生什么。

这就是为什么multithreading编程“很难”的本质。 它经常违反临时测试,甚至大多数unit testing。 有效地实现它的唯一方法是了解整个软件和硬件堆栈,并通过使用状态机来绘制每种可能的情况。

总而言之,线程编程并不是关于您所见过的事情,而是关于可能发生的事情,无论多么不可能。

事实上,你的简单代码没有表现出不稳定的行为并不意味着什么。 您的代码太简单,与volatile无关。 您需要编写非常计算密集的代码来创建清晰可见的内存竞争条件。

此外, volatile关键字在x86 / x64以外的其他平台上可能对其他内存模型有用。 (我的意思是像Itanium一样。)

Joe Duffy在他的博客上写了关于volatile有趣信息。 我强烈建议阅读它。

然后使用断点我跟踪线程。 我的初始概念是所有三个线程将同时被触发,但我的断点流程显示线程执行流程一个接一个地跟随(输出格式也是如此,即从上到下执行线程)。 伙计们为什么会这样?

调试器暂时挂起线程以使其更容易调试。

我怀疑volatile关键字没有实际的实际用途。 我在某个地方出错了吗?

Console.WriteLine调用很可能修复掩盖问题。 它们很可能隐含地为您生成必要的内存屏障。 下面是一段非常简单的代码片段,演示了当volatile不用于声明stop变量时实际上存在问题。

使用Release配置编译以下代码,并在调试器外部运行它。

 class Program { static bool stop = false; public static void Main(string[] args) { var t = new Thread(() => { Console.WriteLine("thread begin"); bool toggle = false; while (!stop) { toggle = !toggle; } Console.WriteLine("thread end"); }); t.Start(); Thread.Sleep(1000); stop = true; Console.WriteLine("stop = true"); Console.WriteLine("waiting..."); t.Join(); } }