我有以下示例程序:
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
。
为什么?
答案 0 :(得分:2)
t1之前没有理由不能开始运行t2。使用TPL库甚至更可能的情况(首先在LIFO堆栈上推送任务)。
调试器可以延迟/改变事物以产生另一个结果。
任务和线程通常会误导这种方式。
答案 1 :(得分:1)
t2
在t1
之前开始,因此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();
}
}
}