Como posso converter esse código foreach para Parallel.ForEach?

180

Estou um pouco confuso Parallel.ForEach.
O que é Parallel.ForEache o que exatamente faz?
Por favor, não faça referência a nenhum link do MSDN.

Aqui está um exemplo simples:

string[] lines = File.ReadAllLines(txtProxyListPath.Text);
List<string> list_lines = new List<string>(lines);

foreach (string line in list_lines)
{
    //My Stuff
}

Como posso reescrever este exemplo com Parallel.ForEach?

Luz cinza
fonte
Isso pode ter sido respondido aqui stackoverflow.com/questions/3789998/…
Ujjwal Manandhar
1
@UjjwalManandhar Na verdade, é bem diferente, pois está perguntando sobre a diferença entre a Parallelclasse e o uso do PLINQ.
Reed Copsey
18
Outros responderam como você pode reescrever. Então o que isso faz? Ele realiza uma "ação" em cada item da coleção, como normalmente foreach. A diferença é que a versão paralela pode executar muitas "ações" ao mesmo tempo. Na maioria dos casos (dependendo de qual computador está executando o código, de quão ocupado ele está e de outras coisas), será mais rápido e essa é a vantagem mais importante. Observe que, ao fazer isso em paralelo, não é possível saber em que ordem os itens são processados. Com um habitual (serial) foreach, você tem a garantia de que lines[0]vem primeiro, depois lines[1]e assim por diante.
Jeppe Stig Nielsen
1
@JeppeStigNielsen Nem sempre será mais rápido, pois há uma sobrecarga significativa em tornar as coisas paralelas. Depende do tamanho da coleção na qual você está iterando e da ação nela. O correto é medir a diferença entre usar Parallel.ForEach () e usar foreach (). Muitas vezes, um foreach normal () é mais rápido.
Dave Black
3
@DaveBlack Sure. É preciso medir se é mais rápido ou mais lento, em cada caso. Eu estava apenas tentando descrever paralelização em geral.
Jeppe Stig Nielsen

Respostas:

126
string[] lines = File.ReadAllLines(txtProxyListPath.Text);
List<string> list_lines = new List<string>(lines);
Parallel.ForEach(list_lines, line =>
{
    //Your stuff
});
LIBRA
fonte
6
Só queria indicá-lo (mais para o OP) de modo que não havia um pensamento equivocado de que ele só funciona em List<T>;)
Reed Copsey
1
obrigado pela atenção e resposta. usei List <string> nos meus códigos por causa da remoção de itens duplicados usando listas HASH. com matriz regular, não podemos remover duplicatas facilmente :).
SilverLight
119
Estou confuso que esta resposta é marcado como a resposta certa, uma vez que não há explicação para o original pergunta mensagens ... "O que é Parallel.ForEach eo que faz isso exatamente?"
fose
6
@fosb O problema é que o título da pergunta foi editado para alterar completamente o significado ... então essa resposta não faz mais sentido. Dito isto, ainda é uma resposta pobre
aw04
274

Loop Foreach:

  • As iterações ocorrem sequencialmente, uma a uma
  • O loop foreach é executado a partir de um único Thread.
  • O loop foreach é definido em todas as estruturas do .NET
  • A execução de processos lentos pode ser mais lenta , pois eles são executados em série
    • O processo 2 não pode ser iniciado até que 1 seja concluído. O processo 3 não pode ser iniciado até que 2 e 1 estejam concluídos ...
  • A execução de processos rápidos pode ser mais rápida , pois não há sobrecarga de segmentação

Parallel.ForEach:

  • A execução ocorre de maneira paralela.
  • Parallel.ForEach usa vários segmentos.
  • Parallel.ForEach é definido nas estruturas .Net 4.0 e acima.
  • A execução de processos lentos pode ser mais rápida , pois pode ser executada em paralelo
    • Os processos 1, 2 e 3 podem ser executados simultaneamente (consulte threads reutilizados no exemplo abaixo)
  • A execução de processos rápidos pode ser mais lenta , devido a sobrecarga adicional de segmentação

O exemplo a seguir demonstra claramente a diferença entre o loop foreach tradicional e

Exemplo Parallel.ForEach ()

using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace ParallelForEachExample
{
    class Program
    {
        static void Main()
        {
            string[] colors = {
                                  "1. Red",
                                  "2. Green",
                                  "3. Blue",
                                  "4. Yellow",
                                  "5. White",
                                  "6. Black",
                                  "7. Violet",
                                  "8. Brown",
                                  "9. Orange",
                                  "10. Pink"
                              };
            Console.WriteLine("Traditional foreach loop\n");
            //start the stopwatch for "for" loop
            var sw = Stopwatch.StartNew();
            foreach (string color in colors)
            {
                Console.WriteLine("{0}, Thread Id= {1}", color, Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(10);
            }
            Console.WriteLine("foreach loop execution time = {0} seconds\n", sw.Elapsed.TotalSeconds);
            Console.WriteLine("Using Parallel.ForEach");
            //start the stopwatch for "Parallel.ForEach"
             sw = Stopwatch.StartNew();
            Parallel.ForEach(colors, color =>
            {
                Console.WriteLine("{0}, Thread Id= {1}", color, Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(10);
            }
            );
            Console.WriteLine("Parallel.ForEach() execution time = {0} seconds", sw.Elapsed.TotalSeconds);
            Console.Read();
        }
    }
}

Resultado

Traditional foreach loop
1. Red, Thread Id= 10
2. Green, Thread Id= 10
3. Blue, Thread Id= 10
4. Yellow, Thread Id= 10
5. White, Thread Id= 10
6. Black, Thread Id= 10
7. Violet, Thread Id= 10
8. Brown, Thread Id= 10
9. Orange, Thread Id= 10
10. Pink, Thread Id= 10
foreach loop execution time = 0.1054376 seconds

Usando o exemplo Parallel.ForEach

1. Red, Thread Id= 10
3. Blue, Thread Id= 11
4. Yellow, Thread Id= 11
2. Green, Thread Id= 10
5. White, Thread Id= 12
7. Violet, Thread Id= 14
9. Orange, Thread Id= 13
6. Black, Thread Id= 11
8. Brown, Thread Id= 10
10. Pink, Thread Id= 12
Parallel.ForEach() execution time = 0.055976 seconds
Jignesh.Raj
fonte
63
Eu realmente não concordo com a sua 'afirmação' de que Parallel.ForEach é (sempre) mais rápido. Isso realmente depende do peso da operação dentro do loop. Isso pode ou não valer a sobrecarga da introdução do paralelismo.
Martão
1
Bem, o paralelo para cada um significa que threads separados são configurados para executar o código no corpo do loop. Embora o .NET tenha um mecanismo eficiente para fazer isso, isso é uma sobrecarga considerável. Portanto, se você precisar apenas de uma operação simples (por exemplo, uma soma ou multiplicação), o foreach paralelo não deve ser mais rápido.
Martão
3
@ Jignesh, esse nem é um bom exemplo de medição, então eu não me referiria a isso. Remova "Thread.Sleep (10);" de cada corpo do loop e tente novamente.
stenly
1
@ Martao está certo, o problema é com sobrecargas de bloqueio de objetos em que a abordagem paralela pode ser maior que a seqüencial.
stenly
8
@stenly Eu acho que o sono é precisamente a razão pela qual é um bom exemplo. Você não usaria um PFE com iterações únicas rápidas (como Martao explicou) - portanto, essa resposta está tornando a iteração lenta e a vantagem (correta) do PFE é destacada. Concordo que, embora isso precise ser explicado na resposta, um negrito "é sempre mais rápido" é muito enganador.
mafu
43
string[] lines = File.ReadAllLines(txtProxyListPath.Text);

// No need for the list
// List<string> list_lines = new List<string>(lines); 

Parallel.ForEach(lines, line =>
{
    //My Stuff
});

Isso fará com que as linhas sejam analisadas em paralelo, dentro do loop. Se você deseja uma introdução mais detalhada e menos "orientada a referência" à classe Parallel, escrevi uma série sobre o TPL que inclui uma seção em Parallel.ForEach .

Reed Copsey
fonte
9

Para arquivos grandes, use o seguinte código (você tem menos memória)

Parallel.ForEach(File.ReadLines(txtProxyListPath.Text), line => {
    //Your stuff
});
Samuel LEMAITRE
fonte
2

Essas linhas funcionaram para mim.

string[] lines = File.ReadAllLines(txtProxyListPath.Text);
var options = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount * 10 };
Parallel.ForEach(lines , options, (item) =>
{
 //My Stuff
});
Prince Prasad
fonte