2011-10-23 57 views
0

简单复印面食这里:.NET 4.0:请帮我解决这个问题的任务

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写得不好。

回答

3
() => 
{ 
    int i = r.Next(0, 3000); 
    Thread.Sleep(i); 
    Console.WriteLine("{0}: {1}", o, i); 
}) 

你是closing over your loop variableo与您用于任务委托 - 在一次执行你的循环已完成,你只得到最终值5 o。请记住,您正在通过循环变量创建闭包,而不是当前的 - 仅在任务启动后执行委托时才会评估该值。

你必须创建循环变量的本地副本,而不是,然后你就可以安全使用:

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); })); 
} 
+0

我是不知道我明白。你能提供代码,以便我能看到你的意思吗? – oscilatingcretin

+0

工作正常!我仍然不明白这个“关闭循环变量”的东西,但我只需要阅读更多的内容。 – oscilatingcretin

+0

@ scilatingcretin:Eric Lippert的文章链接上面,让人有一个好读 – BrokenGlass

2

这里至少有两个问题。

o在每次迭代中的值为5的问题是词法关闭的那些“陷阱”之一。如果你想o捕捉其当前值,您必须在循环内创建一个本地变量和使用,在您的拉姆达,例如:

for (int o = 0; o < 5; ++o) 
{ 
    int localO = o; 
    // now use "localO" in your lambda ... 
} 

此外,Random不是线程安全的。在多个线程中同时使用同一个Random实例可能会损坏其状态并给您带来意想不到的结果。

1

我认为你正在创建它们的任务是在顺序执行的假设,以及TPL没有这样的保证...

至于“O”参数始终打印为5,即是因为它是匿名函数的父范围中的局部变量,因此当打印实际执行时,它的值为5,因为循环已完成(与'i'在作用范围内的匿名函数相比)

+0

我并不期望他们按照他们创建的顺序执行,但显示o以随机顺序打印为0-4。不过,我发现我在那部分做错了。 – oscilatingcretin