【问题标题】:Infinite loop in release mode释放模式下的无限循环
【发布时间】:2012-05-25 04:16:35
【问题描述】:

当我在 debug 模式下运行以下代码时,它将成功完成并退出。但是,如果我在 release 模式下运行以下代码,它会陷入无限循环并且永远不会完成。

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

    new Thread(() =>
    {
        Thread.Sleep(1000);
        stop = true;
        Console.WriteLine("Set \"stop\" to true.");

    }).Start();

    Console.WriteLine("Entering loop.");

    while (!stop)
    {
    }

    Console.WriteLine("Done.");
}

哪种优化导致它陷入无限循环?

【问题讨论】:

  • 线程间是否不需要同步对stop的访问?
  • 这是一些优化的原因。编译将构建一个真正的停止状态。
  • 看看新的 CancellationToken 类。他们被发明来解决这个问题。 msdn.microsoft.com/en-us/library/dd997364.aspx

标签: c# .net multithreading


【解决方案1】:

我的猜测是主线程上stop 变量的处理器缓存。在调试模式下,内存模型更加严格,因为调试器需要能够提供跨所有线程的变量状态的合理视图。

尝试创建一个字段并将其标记为volatile:

volatile bool stop = false;

static void Main(string[] args)
{

    new Thread(() =>
    {
        Thread.Sleep(1000);
        stop = true;
        Console.WriteLine("Set \"stop\" to true.");

    }).Start();

    Console.WriteLine("Entering loop.");

    while (!stop)
    {
    }

    Console.WriteLine("Done.");
}

【讨论】:

  • Volatile 有利于正确性,但为了避免优化,让它看起来像字段而不是局部变量就足够了。
  • @AlexeiLevenkov 我可能弄错了,但我相信这是 CLR 的一个实现细节,理论上(尽管可能不是实际)可能会发生变化。
  • 我想知道您为什么不同步对变量的访问。我总是这样做,因为我认为获取/设置bool 变量不是原子操作。我错了吗?
  • 我认为很难找到一种合理的非原子方式来设置或获取布尔变量...缓存(由 CPU 或编译器本身)是导致这个。
  • @UweKeim:获取/设置布尔值绝对是原子的。它在规范中。 stackoverflow.com/questions/9666/…
【解决方案2】:

因为它不是线程安全的,所以您在子线程中更新主线程变量stop。它总是无法预测的。要处理此类情况,请查看this article。

volatile 关键字指示编译器生成一个 每次读取该字段的获取栅栏,并在 每次写入该字段。获取栅栏可防止其他 从栅栏前移动读取/写入;释放围栏 防止其他读/写在栅栏之后移动。这些 “半栅栏”比全栅栏更快,因为它们给 运行时和硬件有更多优化空间。

【讨论】:

    【解决方案3】:

    线程不安全代码是不可预测的。主要问题是从另一个线程更改一个线程变量。使变量全局或易变。您可以通过以下方式进行操作

    static volatile bool stop = false;
    
    static void Main(string[] args)
    {    
        new Thread(() =>
        {
            Thread.Sleep(1000);
            stop = true;
            Console.WriteLine("Set \"stop\" to true.");
    
        }).Start();
    
        Console.WriteLine("Entering loop.");
    
        while (!stop)
        {
        }
    
        Console.WriteLine("Done.");
    }
    

    【讨论】:

      【解决方案4】:

      看起来对局部变量的值进行了某种优化 - 更改为字段使其终止正常(注意在实际代码中应使用易失性或正确的锁定):

      using System;
      using System.Threading;
      
      class Program
      {
          static bool stop = false;
          static void Main(string[] args)
          {
      
              new Thread(() =>
              {
                  Thread.Sleep(1000);
                  stop = true;
                  Console.WriteLine("Set \"stop\" to true.");
      
              }).Start();
      
              Console.WriteLine("Entering loop.");
      
              while (!stop)
              {
              }
      
              Console.WriteLine("Done.");
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-11-13
        • 1970-01-01
        • 1970-01-01
        • 2023-02-01
        • 1970-01-01
        • 2020-01-13
        • 1970-01-01
        相关资源
        最近更新 更多