使用并行任务库时'foreach'失败

时间:2010-06-02 09:17:15

标签: .net-4.0 parallel-processing task-parallel-library

以下代码创建正确数量的文件,但每个文件都包含第一个列表的内容。有人能发现我做错了吗?

private IList<List<string>> GetLists()
{
  // Code omitted for brevity...
}

private void DoSomethingInParallel()
{
  var lists = GetLists();

  var tasks = new List<Task>();

  var factory = new TaskFactory();

  foreach (var list in lists)
  {
    tasks.Add(factory.StartNew(() =>
    {
      WriteListToLogFile(list);
    }));
  }

  Task.WaitAll(tasks.ToArray());
}

4 个答案:

答案 0 :(得分:4)

原因在于C#评估匿名方法的方式,它们不是真正的闭包。它实际上与TPL无关。以下代码打印出所有的d。这不是你期望的那样

List<Task> tasks = new List<Task>();
List<string> lists = new List<string>();
lists.AddRange(new string[] { "a", "b", "c", "d" });

foreach (var list in lists)
{
    tasks.Add(Task.Factory.StartNew(() =>
    {
        Console.WriteLine(list);
    }));
} 

原因是因为创建时匿名方法列表的值不是在方法体中评估的值。使用方法执行列表的值。您可以通过执行以下操作强制修复此问题:

List<Task> tasks = new List<Task>();
List<string> lists = new List<string>();
lists.AddRange(new string[] { "a", "b", "c", "d" });

foreach (var list in lists)
{
    var localList = list;  
    tasks.Add(Task.Factory.StartNew(() =>
    {
        Console.WriteLine(localList);
    }));
} 

您不必明确地将列表值传递给匿名方法。

这篇博文更详细地介绍了这一点:

http://blogs.msdn.com/b/abhinaba/archive/2005/10/18/482180.aspx

答案 1 :(得分:1)

抱歉不回复此问题。我找到了一个解决方案 - 虽然我不明白它为什么会起作用......

最初,我有这个......

foreach (var list in lists)
  {
    tasks.Add(factory.StartNew(() =>
    {
      WriteListToLogFile(list);
    }));
  }

将顺序f​​oreach更改为并行foreach可以解决问题......

Parallel.ForEach<string>(lists, list =>
    tasks.Add(factory.StartNew(() =>
    {
      WriteListToLogFile(list);
    }));
  );

答案 2 :(得分:0)

我不确定为什么你有一个“任务”列表,你只使用其中一个。

编辑: factory.StartNew创建并启动System.Threading.Tasks.Task !!

大声思考: 所以在其列表中的每个List<String>都有一个单独的任务,它调用WriteListToLogFile?

我认为你需要使用

ThreadPool.QueueUserWorkItem 
在task.Add

之后的代码中

查看此示例(请参阅接受的答案帖子)link

答案 3 :(得分:0)

自己也遇到同样的问题。我仍然不确定它为什么会发生,但我能够通过传递一个状态对象让它正常工作

 foreach (var list in lists) 
  { 
    tasks.Add(factory.StartNew((o) => 
    { 
      var l = o as List<string>;
      WriteListToLogFile(l); 
    }, list)); 
  }