.net 4.0 使用并行任务库时“foreach”失败

.net 4.0 使用并行任务库时“foreach”失败,.net-4.0,parallel-processing,task-parallel-library,.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 fact

下面的代码创建了正确数量的文件,但每个文件都包含第一个列表的内容。有人能看出我做错了什么吗

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());
}

我不知道为什么你有一个任务列表,你只使用过其中的一个

编辑: factory.StartNew创建并启动系统。线程。任务。任务

大声思考: 所以列表中的每个列表都有一个单独的任务,调用WriteListToLogFile

我想你需要使用

ThreadPool.QueueUserWorkItem 
在任务后的代码中。添加


看看这个例子,看看被接受的答案帖子,我自己也遇到了同样的问题。我仍然不知道为什么会发生这种情况,但我能够通过传入一个state对象使它正常工作

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

很抱歉没有提前回复。我找到了一个解决方案-虽然我不明白为什么它会起作用

原来,我有这个

foreach (var list in lists)
  {
    tasks.Add(factory.StartNew(() =>
    {
      WriteListToLogFile(list);
    }));
  }
将顺序foreach更改为并行foreach解决了此问题

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

原因在于C计算匿名方法的方式,它们不是真正的闭包。这真的与第三方物流无关。下面的代码打印出所有的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的值不是在方法体中计算的值。使用方法执行时列表的值。您可以通过执行以下操作强制对此进行修复:

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);
    }));
} 
您不必显式地将列表值传递给匿名方法

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


如果您没有正确读取代码,请查看WriteListToLogFile方法。工厂,开始新的。。。为列表集合中的每个项目创建并启动新任务。+1好的,我现在看到我的错误了:谢谢!!var声明让我感到困惑。ThreadPool是执行并行编程的旧方法,没有提供新的并行任务库那么多的反馈和控制。你真的不应该使用parallel.ForEach而不是ForEach。对于像启动任务这样轻量级的事情来说,这将是非常低效的。请参阅我的答案以获得解释。您不应该使用状态对象。见我的答案/解释。