19

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

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.");
}

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

4

4 回答 4

18

我的猜测是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.");
}
于 2012-05-25T04:21:20.520 回答
11

因为它不是线程安全的,所以您更新子线程内的主线程变量stop。它总是无法预测的。要处理这样的任何情况,请查看这篇文章

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

于 2012-05-25T04:21:01.107 回答
0

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

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.");
}
于 2012-05-25T04:37:40.357 回答
0

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

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.");
    }
}
于 2012-05-25T04:53:08.703 回答