【问题标题】:Debug vs Release C# code调试与发布 C# 代码
【发布时间】:2018-06-29 19:52:38
【问题描述】:

我有以下代码:

 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!");

    }

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

这是什么原因?

谢谢。

【问题讨论】:

  • “为什么这个不安全的多线程代码行为不规律”并不是一个真正有用的问题......虽然它是有趣的编码难题,但它的实用价值为零......
  • @AlexeiLevenkov 在生产环境中的现实生活中看到这样的代码,人们在徘徊为什么它是错误的并且不像在他们的开发机器上那样工作,然后决定在生产中运行调试版本,我不会感到惊讶.这一切都只是因为他们没有意识到一些优化。

标签: c#


【解决方案1】:

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

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

你需要向编译器表明这个变量是volatile:本质上是告诉系统根据当前线程中执行的代码做出假设,无论这个内存是否改变(即,其他一些线程或进程可能会改变它)。

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

How do I Understand Read Memory Barriers and Volatile - StackOverflow

如果您想更深入地研究内存和并发性,请阅读 Fabian Giesen 撰写的一篇关于多核系统中缓存一致性的优秀文章:

Cache coherency primer

Atomic operations and contention

除非您知道自己在做什么,否则您应该只使用锁,以下是很好的理由:

Volatile keyword usage vs Lock - StackOverflow

这是关于 Volatile.Read 方法的 MSDN 文档:

Volatile.Read Method()

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

这是 volatile 关键字的 MSDN 文档:

volatile (C# Reference)

在 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 可能的指令重新排序,以及在某些晦涩的平台上甚至内核之间的缓存合并。
  • 局部变量不能声明为 volatile。那不会编译。
  • 他不应该使用关键字,使用半栅栏内存屏障:例如Volatile.Read
  • 我认为如果它真正解释了 volatile 的作用(除了“使程序工作”之外),这将是一个更好的答案。
猜你喜欢
  • 2011-03-26
  • 1970-01-01
  • 2011-03-19
  • 2011-05-07
  • 1970-01-01
  • 2016-08-02
  • 2016-01-23
  • 2015-01-13
  • 1970-01-01
相关资源
最近更新 更多