'foreach' failing when using Parallel Task Library

大兔子大兔子 提交于 2020-01-01 18:59:25

问题


The following code creates the correct number of files, but every file contains the contents of the first list. Can anyone spot what I've done wrong please?

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

回答1:


The reason why is down to the way C# evaluates anonymous methods, they're not true closures. It really has nothing to do with the TPL. The following code prints out all d's. This is not what yoy would expect

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

The reason is because the value of list when the anonymous method was created is not the one that gets evaluated in the method body. The value of list at the time the method was executed is used. You can force a fix for this by doing the following:

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

You don't have to pass in the list value to the anonymous method explicitly.

This blog post goes into this in much more detail:

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




回答2:


Apologies for not replying to this earlier. I found a solution - although I don't understand why it works...

Originally, I had this ...

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

Changing the sequential foreach to a parallel foreach fixes the problem...

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



回答3:


I am not sure why you have a list for "tasks", you are only ever using one of them.

edit: factory.StartNew Creates and starts a System.Threading.Tasks.Task!!

Thinking out loud: so there is a separate task for each of the List<String> in its list which calls WriteListToLogFile?

I think you will need to use

ThreadPool.QueueUserWorkItem 

in your code after task.Add

look at this example (see the accepted answer post) link




回答4:


Ran into this same problem myself. I'm still not sure why it happens, but I was able to get it to work properly by passing in a state object

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


来源:https://stackoverflow.com/questions/2956328/foreach-failing-when-using-parallel-task-library

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!