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

我们真的需要C#中的VOLATILE关键字吗?,c#,multithreading,volatile,C#,Multithreading,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 =

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

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
关键字没有实际的实际用途。我哪里出错了吗?

您的简单代码在volatile中的行为不直接这一事实并不意味着什么。您的代码太简单,与volatile无关。您需要编写计算密集型代码来创建清晰可见的内存竞争条件

此外,
volatile
关键字在x86/x64以外的其他平台上以及其他内存型号上可能很有用。(我的意思是像安腾这样的公司。)


乔·达菲(Joe Duffy)在他的博客上写了关于
volatile
的有趣信息。我强烈建议你读一读。

你的思维方式有缺陷

线程相关问题的本质是它们是不确定的。这意味着,你所观察到的可能不是未来可能发生什么的指标

这就是为什么多线程编程“很难”的本质。它通常会挑战特别测试,甚至大多数单元测试。唯一有效的方法是了解整个软件和硬件堆栈,并通过使用状态机绘制每个可能发生的事件


总之,线程编程不是关于你所看到的事情,而是关于可能发生的事情,不管发生的可能性有多大。

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

数字1:尝试使用调试器检查线程的行为与反复运行多线程程序并得出结论认为它工作正常一样有用,因为100个测试中没有一个失败:错误!线程的行为是完全不确定的(有些人会说是随机的),您需要不同的方法来确保这样的程序能够正确运行

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

从某种意义上说,
volatile
是一种保护,以防代码实际上(而且很可能不会)像您认为的那样在多线程场景中运行

然后使用断点跟踪线程。我最初的构想 所有三个线程都将同时启动,但我的 断点流显示线程执行流遵循一个断点流 在其他之后(以及输出格式,即从上到下执行) 线程数)。伙计们,为什么会这样

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

我怀疑volatile关键字没有实际的现场使用。我要去吗 哪里出错了

Console.WriteLine
调用很可能解决了这个问题。它们很可能会隐式地为您产生必要的记忆障碍。下面是一个非常简单的代码片段,它演示了当不使用
volatile
声明
stop
变量时,实际上存在一个问题

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


在使用调试器时,您怎么会期望这种情况发生?为什么您认为线程会同时执行?那么,为什么在使用和不使用它们时,我的输出没有任何区别?当然,它们不会同时运行。您担心被击中。有一天,你在一个安静的郊区附近散步,没有穿防弹衣,也没有人向你开枪,因此你得出结论,防弹衣在战场上阻止子弹是没有必要的?这在几个方面有点误导。。问题不在于太多的发布模式,而是在没有附加调试程序的情况下运行。无论它是否在不同的内核上运行,分时多线程在这里都有完全相同的问题,因为这不是一个有趣的全局排序问题,而是一个“值在寄存器中,而不是从缓存中重新读取,所以它永远不会改变”的问题。@harold:在调试器这一点上,我不敢苟同。这不是一个附加调试器的问题,而是一个有无优化的编译问题。即使不使用IDE,您也可以重现这个问题。对于C来说,这是正确的,但是C#编译器无论如何都不会优化,不管优化标志会说什么。这都是JIT编译器的错。@harold:我刚刚重现了使用csc/o编译的问题。没有国旗就不会发生。你自己试试。是的,我已经试过了,虽然不是用这段代码,而且只有调试器是重要的。谢谢大家。。。我的结论是,线程执行完全不确定,完全取决于处理器类型、速度和使用的代码优化技术。易失性可用性可以在大型计算机上观察到,而不是在小型调试器中。再次感谢大家。
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();
    }
}