发布模式下的无限循环

19

当我在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”吗? - Uwe Keim
这是由于一些优化内容引起的。编译器将会构建一个真正的停止实例。 - rekire
看一下新的CancellationToken类。它们被发明来解决这个问题。http://msdn.microsoft.com/en-us/library/dd997364.aspx - Martin Brown
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.");
}

@AlexeiLevenkov 我可能错了,但我认为这是CLR的实现细节,并且理论上(虽然可能不实际)可能会更改。 - Chris Shain
我想知道为什么你没有同步访问变量。我总是这样做,因为我认为获取/设置布尔变量不是原子操作。我错了吗? - Uwe Keim
1
我认为要想出一种合理的非原子方式来设置或获取布尔变量会很困难...这是由于缓存(无论是CPU还是编译器本身)造成的。 - zmbq
2
@UweKeim:获取/设置布尔值肯定是原子操作。这在规范中有说明。https://dev59.com/4HVD5IYBdhLWcg3wWKLc - Ilian
这对我来说非常有用。感谢您的好回答! - Mystic Lin
显示剩余12条评论

11

由于它不是线程安全的,你在子线程中更新主线程变量stop,结果将总是不可预测的。要处理这样的情况,请参考此文章

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


0

线程不安全的代码是不可预测的。主要问题是从另一个线程更改一个线程变量。将该变量设置为全局变量或者volatile变量。您可以按照以下方式实现

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

0

看起来是一种针对本地变量值的优化 - 将其更改为字段可以使其正常终止(请注意,实际代码中应使用volatile或正确的锁定):

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接