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