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

时间:2011-12-16 19:31:08

标签: 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 = 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关键字没有实际用途。我在某个地方出错了吗?

4 个答案:

答案 0 :(得分:4)

你的思维方法存在缺陷。

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

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

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

答案 1 :(得分:3)

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

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

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

从某种意义上说,volatile是一个警卫,以防代码实际上(并且很可能不会)按照您认为在多线程场景中运行的方式运行。

答案 2 :(得分:2)

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

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

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

答案 3 :(得分:0)

  

然后使用断点我跟踪了线程。我最初的构想   是所有的三个线程将同时被解雇,但我的   断点流表明线程执行流程遵循一个   在其他之后(输出格式也是如此,即从上到下执行   线程)。伙计们为什么会这样?

调试器暂时挂起线程以便于调试。

  

我怀疑volatile关键字没有实际的实际用途。我要去   某处错了?

Console.WriteLine电话很可能是 fix 屏蔽问题。它们很可能隐含地为您生成必要的内存屏障。这是一段非常简单的代码片段,表明当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();
    }
}