'foreach' failing when using Parallel Task Library

丶灬走出姿态 提交于 2019-12-04 16:09:24

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

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

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

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