.NET 4.0:请帮我解决这个任务问题

时间:2011-10-23 03:15:02

标签: multithreading .net-4.0 task

简单复制面食:

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 个答案:

答案 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,因为循环已完成(比较''我'在匿名函数范围内)