Comment puis-je convertir ce code foreach en Parallel.ForEach?

180

Je suis un peu confus Parallel.ForEach.
Qu'est- Parallel.ForEachce que c'est et que fait-il exactement?
Veuillez ne faire référence à aucun lien MSDN.

Voici un exemple simple:

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

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

Comment réécrire cet exemple avec Parallel.ForEach?

SilverLight
la source
Cela a peut-être été répondu ici stackoverflow.com/questions/3789998/…
Ujjwal Manandhar
1
@UjjwalManandhar C'est en fait assez différent, car il demande la différence entre la Parallelclasse et l'utilisation de PLINQ.
Reed Copsey
18
D'autres ont répondu comment vous pouvez réécrire. Alors qu'est-ce que ça fait? Il effectue une "action" sur chaque élément de la collection, comme d'habitude foreach. La différence est que la version parallèle peut effectuer plusieurs «actions» en même temps. Dans la plupart des cas (en fonction de l'ordinateur qui exécute le code, de son niveau d'occupation, et d'autres choses), ce sera plus rapide, et c'est l'avantage le plus important. Notez que lorsque vous le faites en parallèle, vous ne pouvez pas savoir dans quel ordre les articles sont traités. Avec un habituel (série) foreach, vous avez la garantie que lines[0]vient en premier, puis lines[1], et ainsi de suite.
Jeppe Stig Nielsen
1
@JeppeStigNielsen Ce ne sera pas toujours plus rapide car il y a une surcharge importante pour rendre les choses parallèles. Cela dépend de la taille de la collection sur laquelle vous effectuez une itération et de l'action qu'elle contient. La bonne chose à faire est de mesurer la différence entre l'utilisation de Parallel.ForEach () et celle de foreach (). Plusieurs fois, un foreach () normal est plus rapide.
Dave Black
3
@DaveBlack Bien sûr. Il faudra mesurer si c'est plus rapide ou plus lent, dans chaque cas. J'essayais juste de décrire la parallélisation en général.
Jeppe Stig Nielsen

Réponses:

126
string[] lines = File.ReadAllLines(txtProxyListPath.Text);
List<string> list_lines = new List<string>(lines);
Parallel.ForEach(list_lines, line =>
{
    //Your stuff
});
KG
la source
6
Je voulais juste le souligner (plus pour l'OP) afin qu'il n'y ait pas une pensée erronée que cela ne fonctionne que List<T>;)
Reed Copsey
1
merci pour l'attention et la réponse. J'ai utilisé List <string> dans mes codes en raison de la suppression des éléments en double à l'aide de listes HASH. avec un tableau régulier, nous ne pouvons pas supprimer les doublons facilement :).
SilverLight
119
Je suis confus que cette réponse est marquée comme la bonne réponse, car il n'y a pas d'explication à la question des messages originaux "Qu'est-ce que Parallel.ForEach et que fait-il exactement?" ...
fose
6
@fosb Le problème est que le titre de la question a été modifié pour en changer complètement le sens ... donc cette réponse n'a plus de sens. Cela dit, c'est toujours une mauvaise réponse
aw04
274

Boucle Foreach:

  • Les itérations ont lieu séquentiellement, une par une
  • La boucle foreach est exécutée à partir d'un seul thread.
  • La boucle foreach est définie dans chaque framework de .NET
  • L'exécution des processus lents peut être plus lente , car ils sont exécutés en série
    • Le processus 2 ne peut pas démarrer tant que 1 n'est pas terminé. Le processus 3 ne peut pas démarrer tant que 2 et 1 ne sont pas terminés ...
  • L'exécution de processus rapides peut être plus rapide , car il n'y a pas de surcharge de thread

Parallel.ForEach:

  • L'exécution a lieu de manière parallèle.
  • Parallel.ForEach utilise plusieurs threads.
  • Parallel.ForEach est défini dans les frameworks .Net 4.0 et supérieurs.
  • L'exécution des processus lents peut être plus rapide , car ils peuvent être exécutés en parallèle
    • Les processus 1, 2 et 3 peuvent s'exécuter simultanément (voir les threads réutilisés dans l'exemple ci-dessous)
  • L'exécution des processus rapides peut être plus lente , en raison de la surcharge de threading supplémentaire

L'exemple suivant montre clairement la différence entre la boucle foreach traditionnelle et

Exemple 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();
        }
    }
}

Production

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

Utilisation de l'exemple 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
la source
63
Je ne suis pas vraiment d'accord avec votre «affirmation» selon laquelle Parallel.ForEach est (toujours) plus rapide. Cela dépend vraiment de la lourdeur de l'opération à l'intérieur de la boucle. Cela peut valoir ou non la surcharge de l'introduction du paralellisme.
Martao
1
Eh bien, le parallèle pour chacun signifie que des threads séparés sont configurés pour exécuter le code dans le corps de la boucle. Même si .NET dispose d'un mécanisme efficace pour ce faire, cela représente une surcharge considérable. Donc, si vous avez juste à une opération simple (par exemple une somme ou une multiplication), le foreach parallèle ne devrait pas être plus rapide.
Martao
3
@Jignesh ce n'est même pas un bon exemple de mesure, donc je n'y ferais pas référence du tout. Retirez "Thread.Sleep (10);" de chaque corps de boucle et réessayez.
stenly le
1
@Martao a raison, le problème vient des frais généraux de verrouillage d'objet où l'approche parallèle peut être plus longue que séquentielle.
stenly
8
@stenly je pense que le Sleep est précisément la raison pour laquelle c'est un bon exemple. Vous n'utiliseriez pas un PFE avec des itérations simples rapides (comme l'explique Martao) - donc cette réponse ralentit l'itération et l'avantage (correct) du PFE est mis en évidence. Je conviens cependant que cela doit être expliqué dans la réponse, un gras "est toujours plus rapide" est très trompeur.
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
});

Cela entraînera l'analyse des lignes en parallèle, dans la boucle. Si vous voulez une introduction plus détaillée et moins "orientée référence" à la classe Parallel, j'ai écrit une série sur le TPL qui comprend une section sur Parallel.ForEach .

Reed Copsey
la source
9

Pour les gros fichiers, utilisez le code suivant (vous avez moins besoin de mémoire)

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

Ces lignes ont fonctionné pour moi.

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