Первый вариант гораздо лучше.
Parallel.ForEach, внутренне, использует Partitioner
для распространения вашей коллекции на рабочие элементы. Он не будет выполнять одну задачу для каждого элемента, а скорее сделает это, чтобы снизить задействованные служебные данные.
Второй вариант запланирует один Task
на элемент в вашей коллекции. Хотя результаты будут (почти) одинаковыми, это принесет гораздо больше накладных расходов, чем необходимо, особенно для больших коллекций, и приведет к замедлению общего времени выполнения.
FYI. Используемый разделитель можно контролировать с помощью используя соответствующие перегрузки для Parallel.ForEach , если это необходимо. Подробнее см. Пользовательские разделители в MSDN.
Основное различие во время выполнения - это второе действие будет асинхронным. Это можно продублировать с помощью Parallel.ForEach, выполнив:
Task.Factory.StartNew( () => Parallel.ForEach- (items, item => DoSomething(item)));
Поступая таким образом, вы по-прежнему пользуетесь разделителями, но не блокируете до завершения операции.