¿Cómo puedo limitar Parallel.ForEach?

295

Tengo un bucle asíncrono Parallel.ForEach () con el que descargo algunas páginas web. Mi ancho de banda es limitado, por lo que puedo descargar solo x páginas por vez, pero Parallel.ForEach ejecuta una lista completa de las páginas web deseadas.

¿Hay alguna manera de limitar el número de hilos o cualquier otro limitador mientras se ejecuta Parallel.ForEach?

Código de demostración:

Parallel.ForEach(listOfWebpages, webpage => {
  Download(webpage);
});

La tarea real no tiene nada que ver con las páginas web, por lo que las soluciones creativas de rastreo web no ayudarán.

eugeneK
fuente
@jKlaus Si la lista no se modifica, por ejemplo, es solo un conjunto de URL, ¿realmente no puedo ver el problema?
Shiv
@Shiv, con el tiempo suficiente ... contará el número de ejecuciones y compárelo con el recuento de la lista.
jKlaus
@jKlaus ¿Qué estás diciendo que saldrá mal?
Shiv
1
@jKlaus está modificando un elemento no seguro para subprocesos (el entero). Esperaría que no funcione en ese escenario. El OP, por otro lado, no modifica nada que deba ser seguro para subprocesos.
Shiv
2
@jKlaus Aquí hay un ejemplo de Parallel.ForEach que establece el recuento correctamente> dotnetfiddle.net/moqP2C . Enlace MSDN: msdn.microsoft.com/en-us/library/dd997393(v=vs.110).aspx
jhamm

Respuestas:

564

Puede especificar a MaxDegreeOfParallelismen un ParallelOptionsparámetro:

Parallel.ForEach(
    listOfWebpages,
    new ParallelOptions { MaxDegreeOfParallelism = 4 },
    webpage => { Download(webpage); }
);

MSDN: Parallel.ForEach

MSDN: ParallelOptions.MaxDegreeOfParallelism

Nicholas Butler
fuente
59
Puede que no se aplique a este caso en particular, pero pensé que lo tiraría en caso de que alguien se pregunte y lo encuentre útil. Aquí estoy utilizando el 75% (redondeado) del recuento de procesadores. var opts = new ParallelOptions { MaxDegreeOfParallelism = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * 0.75) * 1.0)) };
jKlaus
44
Solo para evitar que alguien más tenga que buscarlo en la documentación, pasar un valor de -1es lo mismo que no especificarlo: "Si [el valor] es -1, no hay límite en el número de operaciones que se ejecutan simultáneamente".
stuartd
La documentación no me deja claro: establecer MaxDegreeOfParallelism en 4 (por ejemplo) significa que habrá 4 subprocesos cada uno ejecutando 1/4 de las iteraciones del bucle (se despacha una ronda de 4 subprocesos), o cada subproceso sigue haciendo un bucle iteración y solo estamos limitando cuántos corren en paralelo?
Hashman
77
Para ser claros, los núcleos y los hilos no son lo mismo. Dependiendo de la CPU, hay un número diferente de subprocesos por núcleo, generalmente 2 por núcleo. Por ejemplo, si tiene una CPU de 4 núcleos con 2 hilos por núcleo, entonces tiene un máximo de 8 hilos. Para ajustar el comentario de @jKlaus var opts = new ParallelOptions { MaxDegreeOfParallelism = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * 0.75) * 2.0)) };. Enlace a hilos vs núcleos - askubuntu.com/questions/668538/…
TheMiddleMan
41

Puede usar ParallelOptions y establecer MaxDegreeOfParallelism para limitar el número de hilos concurrentes:

Parallel.ForEach(
    listOfwebpages, 
    new ParallelOptions{MaxDegreeOfParallelism=2}, 
    webpage => {Download(webpage);});     
rikitikitik
fuente
21

Use otra sobrecarga de Parallel.Foreacheso toma una ParallelOptionsinstancia y configúrelo MaxDegreeOfParallelismpara limitar cuántas instancias se ejecutan en paralelo.

Ricardo
fuente
11

Y para los usuarios de VB.net (la sintaxis es extraña y difícil de encontrar) ...

Parallel.ForEach(listOfWebpages, New ParallelOptions() With {.MaxDegreeOfParallelism = 8}, Sub(webpage)
......end sub)  
usuario3496060
fuente