Запуск задач в 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); } 
  • Может ли x86 переупорядочить узкий магазин с более широкой нагрузкой, которая полностью его содержит?
  • Каковы различия между различными параметрами синхронизации streamов в C #?
  • Зеленые темы против не зеленых нитей
  • Синхронизация и System.out.println
  • Реализации Java Queue, какой?
  • ThreadLocal и утечка памяти
  • Монитор против Mutex в c #
  • Блокирует Console.WriteLine?
  • Когда использовать Task.Delay, когда использовать Thread.Sleep?
  • Java Singleton и синхронизация
  • Ожидание выполнения нескольких streamов в Java
  • Interesting Posts

    Отключение выбора текста в PhoneGap

    Пауза для всех задач, отличных от x ЦП

    Зачем отмечать локальные переменные и параметры метода как «final» в Java?

    Технически какая разница между s3n, s3a и s3?

    Получение рабочих элементов и связанных с ними рабочих элементов в одном запросе с использованием API TFS

    Почему Intel Turbo Boost не работает на моем ноутбуке?

    Событие jquery toggle возится со значением флажка

    Как сделать вложенную библиотеку в Windows 7?

    Почему Windows 7 всегда автоматически меняет язык ввода или клавиатуры?

    Как определить, является ли какая-либо из моих операций самой фронтальной и видимой для пользователя?

    java.lang.NoSuchMethodError: статический метод setOnApplyWindowInsetsListener

    Клавиши Alt не работают на терминале gnome с Vim

    Лучший способ использовать размещенный jQuery Google, но вернуться к моей размещенной библиотеке при сбое Google

    Просмотр всех атрибутов файлов в Windows 7 Explorer

    Изменение матрицы 3d на матрицу 2d

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