Como posso limitar o Parallel.ForEach?

295

Eu tenho um loop assíncrono Parallel.ForEach () com o qual baixo algumas páginas da web. Minha largura de banda é limitada para que eu possa baixar apenas x páginas por vez, mas o Parallel.ForEach executa uma lista completa de páginas da web desejadas.

Existe uma maneira de limitar o número de threads ou qualquer outro limitador durante a execução do Parallel.ForEach?

Código de demonstração:

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

A tarefa real não tem nada a ver com páginas da Web, portanto, soluções criativas de rastreamento da Web não ajudarão.

eugeneK
fonte
@jKlaus Se a lista não for modificada, por exemplo, é apenas um conjunto de URLs, não vejo realmente o problema?
Shiv
@Shiv, com tempo suficiente você irá ... Conte o seu número de execuções e compare-o com a contagem da lista.
jKlaus
@jKlaus O que você está dizendo que vai dar errado?
Shiv
1
@jKlaus você está modificando um elemento não seguro para thread (o número inteiro). Eu esperaria que não funcionasse nesse cenário. O OP, por outro lado, não está modificando nada que precise ser seguro para threads.
Shiv
2
@jKlaus Aqui está um exemplo de Parallel.ForEach que define a contagem corretamente> dotnetfiddle.net/moqP2C . Link do MSDN: msdn.microsoft.com/en-us/library/dd997393(v=vs.110).aspx
jhamm

Respostas:

564

Você pode especificar a MaxDegreeOfParallelismem um ParallelOptionsparâmetro:

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

MSDN: Parallel.ForEach

MSDN: ParallelOptions.MaxDegreeOfParallelism

Nicholas Butler
fonte
59
Pode não se aplicar a esse caso em particular, mas achei que seria descartado caso alguém se perguntasse sobre isso e o considerasse útil. Aqui estou utilizando 75% (arredondado para cima) da contagem de processadores. var opts = new ParallelOptions { MaxDegreeOfParallelism = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * 0.75) * 1.0)) };
Jklaus
4
Apenas para salvar qualquer pessoa que procure na documentação, passar um valor -1igual a não especificá-lo: "Se [o valor] for -1, não há limite para o número de operações em execução simultânea"
Stuartd
Não está claro para mim na documentação - definir MaxDegreeOfParallelism como 4 (por exemplo) significa que haverá 4 threads cada executando 1/4 das iterações do loop (uma rodada de 4 threads despachadas) ou cada thread ainda faz um loop iteração e estamos limitando quantas rodam em paralelo?
Hashman
7
Ser núcleos e threads claros não é a mesma coisa. Dependendo da CPU, há um número diferente de threads por núcleo, geralmente 2 por núcleo. Por exemplo, se você possui uma CPU de 4 núcleos com 2 threads por núcleo, possui no máximo 8 threads. Para ajustar o comentário @jKlaus var opts = new ParallelOptions { MaxDegreeOfParallelism = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * 0.75) * 2.0)) };. Link para threads vs núcleos - askubuntu.com/questions/668538/…
TheMiddleMan
41

Você pode usar ParallelOptions e configurar MaxDegreeOfParallelism para limitar o número de threads simultâneos:

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

Use outra sobrecarga Parallel.Foreachque leva uma ParallelOptionsinstância e defina MaxDegreeOfParallelismpara limitar quantas instâncias são executadas em paralelo.

Richard
fonte
11

E para os usuários do VB.net (a sintaxe é estranha e difícil de encontrar) ...

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