简单复制面食:
static void Main(string[] args)
{
List<Task> Tasks = new List<Task>();
Random r = new Random();
for (int o = 0; o < 5; o++)
Tasks.Add(Task.Factory.StartNew(() => { int i = r.Next(0, 3000); Thread.Sleep(i); Console.WriteLine("{0}: {1}", o, i); }));
Task.WaitAll(Tasks.ToArray());
Console.Read();
}
当你运行它时,你会得到这样的东西:
5: 98
5: 198
5: 658
5: 1149
5: 1300
我对此不了解什么?当我希望以随机顺序看到数字0到4时,编写o
的每个迭代对所有线程显示为5。
我尝试使用实际方法而不是匿名方法,它也做了同样的事情。我错过了什么?
编辑:我刚发现第一篇文章的问题并编辑了问题,很抱歉,如果你回答了不正确的订单问题。但是,我很好奇为什么o
没有正确写作。
答案 0 :(得分:3)
() =>
{
int i = r.Next(0, 3000);
Thread.Sleep(i);
Console.WriteLine("{0}: {1}", o, i);
})
您的closing over your loop variable o
与您用于执行任务的代理人一样 - 当您执行完循环时,您只获得o
的结束值5。请记住,您正在创建一个关于循环变量的闭包,而不是它当前的值 - 只有在任务启动后执行委托时才会计算该值。
您必须创建循环变量的本地副本,然后可以安全地使用它:
for (int o = 0; o < 5; o++)
{
int localO = o;
Tasks.Add(Task.Factory.StartNew(() => { int i = r.Next(0, 3000); Thread.Sleep(i); Console.WriteLine("{0}: {1}", localO, i); }));
}
答案 1 :(得分:2)
这里至少有两个问题。
o
在每次迭代时值为5的问题是词法闭包的“陷阱”之一。如果希望o
捕获其当前值,则必须在循环中创建一个本地范围的变量并在lambda中使用它,例如:
for (int o = 0; o < 5; ++o)
{
int localO = o;
// now use "localO" in your lambda ...
}
此外,Random
不是线程安全的。在多个线程中同时使用相同的Random
实例可能会破坏其状态并给您带来意想不到的结果。
答案 2 :(得分:1)
我认为你假设任务按创建顺序执行,而TPL没有这样的保证...
对于'o'参数始终打印为5,这是因为它是匿名函数的父作用域中的局部变量,因此当打印实际执行时,其值为5,因为循环已完成(比较''我'在匿名函数范围内)