调试与发布C#代码

调试与发布C#代码,c#,C#,我有以下代码: public static void Main(string[] args) { bool isComplete = false; var t = new Thread(() => { int i = 0; while (!isComplete) i += 0; }); t.Start(); Thread.Sleep

我有以下代码:

 public static void Main(string[] args)
    {
        bool isComplete = false;

        var t = new Thread(() =>
        {
            int i = 0;
            while (!isComplete) i += 0;
        });

        t.Start();

        Thread.Sleep(500);
        isComplete = true;
        t.Join();
        Console.WriteLine("complete!");

    }
程序将在发布模式下挂起,并在调试模式下给出输出(“完成!”)

原因是什么


谢谢。

变量
isComplete
的值很可能被加载到发布版本中的寄存器中,以避免在while循环中从内存读取值的往返过程。

因此,当
isComplete
的值更改为true时,循环将不会“检测”。

您需要向编译器指出此变量是易失的:本质上是告诉系统不根据当前线程中执行的代码做出假设,无论此内存是否更改(即,其他线程或进程可能会更改它)

有关详细信息,请参阅此SA答案:

-堆垛溢出

如果您想深入研究内存和并发性,那么下面是Fabian Giesen关于多核系统中缓存一致性的一篇优秀文章:

以下是一些很好的理由,说明除非你知道自己在做什么,否则你应该使用锁:

-堆垛溢出

这是
Volatile上的MSDN文档。请阅读
方法:

请注意,如果没有对这些术语进行更详细的解释(见上文SA thread或google),MSDN文档中的描述可能很难转化为实际情况

以下是
volatile
关键字上的MSDN文档:

在C#中,这个关键字将使用半篱笆(据我所知);例如,您还可以在C中找到这个关键字,但它只影响编译器优化(不插入内存屏障),因为它最初用于读取内存映射的I/O

示例

        bool isComplete = false;

        var t = new Thread(() =>
        {
            int i = 0;
            while (!Volatile.Read(ref isComplete)) i += 0;
        });

        t.Start();

        Thread.Sleep(500);
        isComplete = true;
        t.Join();
        Console.WriteLine("complete!");

旁注:
volatile
是编写正确的多线程代码的错误解决方案,但对于这个伪示例来说,它是好的。事实上,
volatile
关键字并不是解决并发问题的神奇解决方案。实际问题需要仔细考虑所有可能的执行顺序,编译器或CPU可能的指令重新排序,在某些模糊的平台上,甚至核心之间的缓存合并。局部变量不能声明为可变的。这不会编译。他不应该使用关键字,使用半篱笆内存屏障:ex
Volatile.Read
。我认为这是一个更好的答案,如果它真的解释了
Volatile
的功能(而不是“使程序工作”)。“为什么这个不安全的多线程代码行为不稳定”不是一个真正有用的问题。。。虽然这是一个有趣的编码难题,但它没有任何实际价值…@AlexeiLevenkov如果在生产环境的现实生活中看到这样的代码,人们会疑惑为什么它是错误的,不会像在开发机器上那样工作,然后决定在生产环境中运行调试构建,我不会感到惊讶。这一切都是因为他们没有意识到一些优化。