多线程重入问题

时间:2013-06-04 21:10:38

标签: c# multithreading loops closures task-parallel-library

我正在尝试为某些处理生成不同的线程。我对每个线程中的一些逻辑使用for循环索引 如何在下面的代码中获得不同的线程来打印1,2,3,4,5?每次我运行它,我得到不同的数字作为输出 - 3,3,3,4,6,6& 2,2,3,5,5,6等 我尝试使用锁定对象,但它没有正确执行。任何人都可以帮我解决这个问题。我只是想确保每个线程/任务获得正确的索引。请注意,每个任务都被强制在单独的线程上运行。

List<Task> tasks1 = new List<Task>();
for (int j = 1; j <= 5; j++)
{    
   tasks1.Add(Task.Factory.StartNew(() =>
          {
              Console.WriteLine(j);
          }
          , new CancellationToken()
          , TaskCreationOptions.LongRunning
          , TaskScheduler.Default)
                                   );
}

Task.WaitAll(tasks1.ToArray());
Console.Read();

3 个答案:

答案 0 :(得分:4)

你正在'捕获循环变量'。在lambda中使用j的事实意味着编译器将以不同的方式对待它(实质上,它将被装箱)并且所有线程将使用相同的共享变量。

简短修复:

 for (int j = 1; j <= 5; j++)
 {
     int jCopy = j;

     tasks1.Add(Task.Factory.StartNew(() =>
      {
          Console.WriteLine(jCopy);
      }, new CancellationToken(), TaskCreationOptions.LongRunning, TaskScheduler.Default)

     );
 }

答案 1 :(得分:3)

只是为了完成其他答案。在C#5.0(.NET 4.5)中,关闭foreach循环变量有一个重大变化,但是没有关闭for循环变量。

查看Eric [{3}}及其Lippert. Closing over the loop variable considered harmful

中的详细信息(以及前言更新说明)

请注意,此问题与多线程或TPL(任务并行库)的使用无关。

其他答案和评论提到它之前已经讨论过,但没有链接到任何先前的答案。以下是一些,为了相互联系:

答案 2 :(得分:2)

回答了数百万次。它与闭包有关。更改您的代码如下

 for (int j = 1; j <= 5; j++)
 {
     int temp = j;

     tasks1.Add(Task.Factory.StartNew(() =>
      {
          Console.WriteLine(temp);
      }, new CancellationToken(), TaskCreationOptions.LongRunning, TaskScheduler.Default)

     );
 }

我建议您阅读:Loop variables and Closures