сбой 'foreach' при использовании библиотеки параллельных задач - PullRequest
2 голосов
/ 02 июня 2010

Следующий код создает правильное количество файлов, но каждый файл содержит содержимое первого списка. Кто-нибудь может определить, что я сделал не так, пожалуйста?

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 ]

4 голосов
/ 01 июля 2010

Причина, по которой C # оценивает анонимные методы, не является истинным замыканием.Это действительно не имеет ничего общего с TPL.Следующий код распечатывает все d.Это не то, чего ожидал бы yoy

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 в то время, когда метод выполнялся .Вы можете принудительно исправить это, выполнив следующие действия:

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 голос
/ 17 июня 2010

Извините, что не ответил на это ранее. Я нашел решение - хотя я не понимаю, почему оно работает ...

Первоначально у меня было это ...

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

Изменение последовательного foreach на параллельный foreach устраняет проблему ...

Parallel.ForEach<string>(lists, list =>
    tasks.Add(factory.StartNew(() =>
    {
      WriteListToLogFile(list);
    }));
  );
0 голосов
/ 17 июня 2010

Сам столкнулся с этой же проблемой. Я до сих пор не уверен, почему это происходит, но я смог заставить его работать должным образом, передав объект состояния

 foreach (var list in lists) 
  { 
    tasks.Add(factory.StartNew((o) => 
    { 
      var l = o as List<string>;
      WriteListToLogFile(l); 
    }, list)); 
  } 
0 голосов
/ 02 июня 2010

Я не уверен, почему у вас есть список «заданий», вы используете только одно из них.

редактирование: factory.StartNew Создает и запускает System.Threading.Tasks.Task !!

Мысли вслух: так что для каждого из List<String> в его списке есть отдельная задача, которая вызывает WriteListToLogFile?

Я думаю, вам нужно будет использовать

ThreadPool.QueueUserWorkItem 

в вашем коде после задания. Добавить

посмотрите на этот пример (см. Принятый ответный пост) ссылка

...