Запуск задач в foreach Loop использует значение последнего элемента

Я делаю первую попытку играть с новыми Задачами, но что-то происходит, чего я не понимаю.

Во-первых, код, который довольно прямолинейный. Я передаю список путей к некоторым файлам изображений и пытаюсь добавить задачу для обработки каждого из них:

public Boolean AddPictures(IList paths) { Boolean result = (paths.Count > 0); List tasks = new List(paths.Count); foreach (string path in paths) { var task = Task.Factory.StartNew(() => { Boolean taskResult = ProcessPicture(path); return taskResult; }); task.ContinueWith(t => result &= t.Result); tasks.Add(task); } Task.WaitAll(tasks.ToArray()); return result; } 

Я обнаружил, что если я просто позволю этому запустить, скажем, список из трех путей в модульном тесте, все три задачи используют последний путь в предоставленном списке. Если я пройду (и замедляю обработку цикла), используется каждый путь из цикла.

Может кто-нибудь объяснить, что происходит, и почему? Возможные обходные пути?

Вы закрываете переменную цикла. Не делай этого. Вместо этого возьмите копию:

 foreach (string path in paths) { string pathCopy = path; var task = Task.Factory.StartNew(() => { Boolean taskResult = ProcessPicture(pathCopy); return taskResult; }); task.ContinueWith(t => result &= t.Result); tasks.Add(task); } 

Ваш текущий код захватывает path не его значение при создании задачи, но сама переменная. Эта переменная меняет значение каждый раз, когда вы проходите цикл, поэтому он может легко измениться к моменту, когда вы вызываете делегата.

Принимая копию переменной, вы вводите новую переменную каждый раз, когда вы проходите цикл – когда вы фиксируете эту переменную, она не будет изменена в следующей итерации цикла.

У Эрика Липперта есть пара сообщений в блогах, которые идут в этом намного подробнее: часть 1 ; часть 2 .

Не чувствую себя плохо – это ловит почти всех 🙁

Лямбда, которую вы передаете в StartNew , ссылается на переменную path , которая изменяется на каждой итерации (т. StartNew Ваша lambda использует ссылку path , а не только ее значение). Вы можете создать локальную копию, чтобы не указывать на версию, которая изменится:

 foreach (string path in paths) { var lambdaPath = path; var task = Task.Factory.StartNew(() => { Boolean taskResult = ProcessPicture(lambdaPath); return taskResult; }); task.ContinueWith(t => result &= t.Result); tasks.Add(task); } 
  • Продюсер / Потребительские streamи с использованием очереди
  • C # версия синхронизированного ключевого слова java?
  • Doxygen медленный
  • C ++ 0x не имеет семафоров? Как синхронизировать streamи?
  • Может ли x86 переупорядочить узкий магазин с более широкой нагрузкой, которая полностью его содержит?
  • Как использовать свойство CancellationToken?
  • Как Java использует несколько ядер?
  • Как дождаться завершения ряда streamов?
  • Почему в OpenMP запрещен оператор! =?
  • Как остановить stream, созданный при реализации runnable-интерфейса?
  • Как прервать Console.ReadLine
  • Interesting Posts

    Как заставить состояние зависания с jQuery?

    Windows 7 Черный экран смерти

    Не удалось скопировать файл. Доступ к пути запрещен.

    Angular2 http.get (), map (), subscribe () и наблюдаемый шаблон – базовое понимание

    Что происходит, когда заканчиваются все IP-адреса?

    Не показано приложение с AppCompat

    XML-атрибут и элемент XML

    jquery проверить, если он нажат или нет

    Как вернуть контекстное меню Google Chrome обратно в стиль Windows

    Результат Struts2 INPUT: как он работает? Как обрабатываются ошибки преобразования / проверки?

    str.translate дает TypeError – Translate принимает один аргумент (2 данный), работал в Python 2

    Есть ли инструменты для выравнивания динамических дисков в Windows 7?

    MVVM: привязка к модели при сохранении модели в синхронизации с версией сервера

    Добавить задержку на ярлык на рабочем столе

    Написание сценариев оболочки, которые будут выполняться на любой оболочке (используя несколько строк shebang?)

    Давайте будем гением компьютера.