La primera es una opción mucho mejor.
Parallel.ForEach, internamente, utiliza a Partitioner<T>
para distribuir su colección en elementos de trabajo. No realizará una tarea por elemento, sino que la procesará por lotes para reducir la sobrecarga involucrada.
La segunda opción programará un solo Task
por artículo en su colección. Si bien los resultados serán (casi) los mismos, esto introducirá muchos más gastos generales de los necesarios, especialmente para grandes colecciones, y hará que los tiempos de ejecución generales sean más lentos.
Para su información: el Partitioner utilizado se puede controlar mediante el uso de las sobrecargas apropiadas para Parallel.ForEach , si así lo desea. Para más detalles, vea Particionadores personalizados en MSDN.
La principal diferencia, en tiempo de ejecución, es que el segundo actuará de forma asíncrona. Esto se puede duplicar usando Parallel.ForEach haciendo:
Task.Factory.StartNew( () => Parallel.ForEach<Item>(items, item => DoSomething(item)));
Al hacer esto, aún aprovecha los particionadores, pero no bloquee hasta que se complete la operación.