使用Task.Run的代码在附加调试器时更改输出

时间:2013-07-23 15:03:42

标签: c# debugging task-parallel-library

我有以下示例程序:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace StackoverflowExample
{
    class Program
    {
        static int value = 1;
        static void Main(string[] args)
        {
            Task t1 = Task.Run(() =>
            {
                if (value == 1)
                {
                    Thread.Sleep(1000);
                    value = 2;
                }
            });

            Task t2 = Task.Run(() =>
            {
                value = 3;
            });

            Task.WaitAll(t1, t2);
            Console.WriteLine(value);
            Console.ReadLine();
        }
    }
}

我希望此代码输出2。我认为t1会看到值为1,然后暂停一秒,其中t2将值设置为3,然后t1将其更改回来到2

这是附加调试器时发生的行为(在Visual Studio中命中F5)。但是,当我在没有附加调试器的情况下运行此程序时(在Visual Studio中为Ctrl + F5),输出为3

为什么?

3 个答案:

答案 0 :(得分:2)

t1之前没有理由不能开始运行t2。使用TPL库甚至更可能的情况(首先在LIFO堆栈上推送任务)。

调试器可以延迟/改变事物以产生另一个结果。

任务和线程通常会误导这种方式。

答案 1 :(得分:1)

t2t1之前开始,因此if (value == 1)返回false。

无法保证线程按顺序启动。

答案 2 :(得分:0)

你有一个所谓的“Race Condition”。你有两个行动可能花费不确定的时间,并且取决于谁先开始/结束你会得到不同的结果。

为避免出现这种情况,您需要确保在使用多个线程时代码在适当的位置使用等待。

using System;
using System.Threading;
using System.Threading.Tasks;

namespace StackoverflowExample
{
    class Program
    {
        static AutoResetEvent sequenceLock = new AutoResetEvent(false);
        static int value = 1;
        static void Main(string[] args)
        {
            Task t1 = Task.Run(() =>
            {
                if (value == 1)
                {
                    sequenceLock.Set(); //lets t2 past the WaitOne()
                    Thread.Sleep(1000);
                    value = 2;
                }
            });

            Task t2 = Task.Run(() =>
            {
                sequenceLock.WaitOne(); //Waits for t1 to set the flag.
                value = 3;
            });

            Task.WaitAll(t1, t2);
            Console.WriteLine(value);
            Console.ReadLine();
        }
    }
}