Parallel.foreach brechen?

111

Wie breche ich aus einer parallel.for- Schleife aus?

Ich habe eine ziemlich komplexe Aussage, die wie folgt aussieht:

Parallel.ForEach<ColorIndexHolder>(ColorIndex.AsEnumerable(),
    new Action<ColorIndexHolder>((ColorIndexHolder Element) =>
    {
        if (Element.StartIndex <= I && Element.StartIndex + Element.Length >= I)
        {
            Found = true;
            break;
        }
    }));

Mit Parallel Class kann ich diesen Prozess bei weitem optimieren. Jedoch; Ich kann nicht herausfinden, wie ich die Parallelschleife unterbrechen kann. Die break;Anweisung löst folgenden Syntaxfehler aus:

Keine umschließenden Schleifen, aus denen herausgebrochen oder fortgefahren werden kann

Rasmus Søborg
quelle
1
Würden Sie erwarten, dass ALLE parallelen Instanzen der Schleife gleichzeitig unterbrochen werden?
n8wrl

Antworten:

185

Verwenden Sie die ParallelLoopState.BreakMethode:

 Parallel.ForEach(list,
    (i, state) =>
    {
       state.Break();
    });

Oder in Ihrem Fall:

Parallel.ForEach<ColorIndexHolder>(ColorIndex.AsEnumerable(),
    new Action<ColorIndexHolder, ParallelLoopState>((ColorIndexHolder Element, ParallelLoopState state) =>
    {
        if (Element.StartIndex <= I && Element.StartIndex + Element.Length >= I)
        {
            Found = true;
            state.Break();
        }
    }));
Tudor
quelle
genau. wollte das selbst posten.
Mare Infinitus
1
Wenn Sie an eine sequentielle foreach-Schleife denken, wird garantiert, dass die Elemente vor dem Element, die aus irgendeinem Grund die Unterbrechung verursacht haben, verarbeitet werden. Was ist mit einer Parallel.ForEach, bei der die Reihenfolge der Artikel nicht unbedingt die Reihenfolge sein muss, in der sie verarbeitet werden? Ist auch garantiert, dass alle Elemente in einem IEnumerable <...> vor dem Element, das state.Break () aufruft, verarbeitet werden und diejenigen, die danach kommen, nicht? Obwohl Ersteres irgendwie erreicht werden könnte, sehe ich nicht, wie Letzteres überhaupt möglich wäre.
Hendrik Wiese
4
@ Hendrik Wiese: Docs sagen: Calling the Break method informs the for operation that iterations after the current one don't have to execute. However, all iterations before the current one will still have to be executed if they haven't already.undthere is no guarantee that iterations after the current one will definitely not execute.
Tudor
2
Dann wäre state.Stop()es angemessener, die erwarteten Ergebnisse zuverlässig zu erzielen, wie unten von Mike Perrenoud und MBentley erwähnt
xtreampb
44

Sie tun dies, indem Sie mit der Überladung von Parallel.Foroder aufrufen, die Parallel.ForEachin einem Schleifenzustand übergeben wird, und dann ParallelLoopState.Breakoder aufrufen ParallelLoopState.Stop. Der Hauptunterschied besteht darin, wie schnell die Dinge brechen - mit verarbeitet Break()die Schleife alle Elemente mit einem früheren "Index" als dem aktuellen. Mit Stop()wird es so schnell wie möglich beendet.

Weitere Informationen finden Sie unter Gewusst wie: Stoppen oder Unterbrechen einer Parallel.For-Schleife .

Reed Copsey
quelle
3
+1, es sieht so aus, als hätten einige von uns hier genau die gleiche Antwort :) - Oh, und ich habe dich bei diesem anderen Kommentator unterstützt.
Mike Perrenoud
Danke für diese Erklärung. Wissen Sie, wann entweder break oder stop aufgerufen werden, ob die aktuell ausgeführten Iterationen abgeschlossen sind oder ob die Iterationen auf halbem Weg durch die Ausführung gestoppt werden?
CeejeeB
1
@CeejeeB Derzeit werden die ausgeführten Vorgänge abgeschlossen.
Reed Copsey
12

Was Sie verwenden sollten, ist Anyanstelle einer foreach-Schleife:

bool Found = ColorIndex.AsEnumerable().AsParallel()
    .Any(Element => Element.StartIndex <= I 
      && Element.StartIndex + Element.Length >= I);

Any ist klug genug, um aufzuhören, sobald es weiß, dass das Ergebnis wahr sein muss.

Servieren
quelle
10

LoopState ist sicherlich eine gute Antwort. Ich fand, dass die vorherigen Antworten so viele andere Dinge enthielten, dass es schwierig war, die Antwort zu sehen. Hier ist ein einfacher Fall:

using System.Threading.Tasks;

Parallel.ForEach(SomeTable.Rows(), (row, loopState) =>
{
    if (row.Value == testValue)
    {
        loopState.Stop();  // Stop the ForEach!
    }       
    // else do some other stuff here.
});
MBentley
quelle
5

Verwenden Sie einfach die loopState , was zur Verfügung gestellt werden kann.

Parallel.ForEach<ColorIndexHolder>(ColorIndex.AsEnumerable(),  
    new Action<ColorIndexHolder>((Element, loopState) => { 
        if (Element.StartIndex <= I && Element.StartIndex + Element.Length >= I) { 
            loopState.Stop();
        }     
})); 

In diesem MSDN-Artikel finden Sie ein Beispiel.

Mike Perrenoud
quelle