Przerwij równolegle. Przed?

111

Jak wyjść z pętli parallel.for ?

Mam dość złożone oświadczenie, które wygląda następująco:

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

Korzystając z klasy równoległej, mogę zdecydowanie zoptymalizować ten proces. Jednak; Nie mogę dowiedzieć się, jak przerwać pętlę równoległą? break;Oświadczenie wyrzuca następujący błąd składni:

Żadnych otaczających pętli, z których można by przerwać lub kontynuować

Rasmus Søborg
źródło
1
Czy spodziewałbyś się, że WSZYSTKIE równoległe wystąpienia pętli zostaną przerwane w tym samym czasie?
n8wrl

Odpowiedzi:

185

Użyj ParallelLoopState.Breakmetody:

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

Lub w twoim przypadku:

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
źródło
dokładnie. miałem zamiar sam to opublikować.
Mare Infinitus
1
Myśląc o sekwencyjnej pętli foreach, gwarantuje się, że elementy znajdujące się przed elementem, który z jakiegokolwiek powodu spowodował przerwanie, zostaną przetworzone. A co z Parallel.ForEach, gdzie kolejność elementów niekoniecznie musi odpowiadać kolejności, w jakiej są przetwarzane? Czy jest również zagwarantowane, że wszystkie elementy w IEnumerable <...> przed tym, który wywołuje stan.Break () są przetwarzane, a te, które pojawiają się po nim, nie? Chociaż to pierwsze dałoby się jakoś osiągnąć, nie widzę w ogóle, jak to drugie byłoby możliwe.
Hendrik Wiese,
4
@Hendrik Wiese: Docs say: 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.ithere is no guarantee that iterations after the current one will definitely not execute.
Tudor
2
więc byłoby state.Stop()bardziej odpowiednie, aby wiarygodnie osiągnąć oczekiwane wyniki, jak wspomnieli poniżej Mike Perrenoud i MBentley
xtreampb
44

Robisz to, wywołując przy użyciu przeciążenia Parallel.Forlub, Parallel.ForEachktóre przechodzi w stanie pętli, a następnie wywołując ParallelLoopState.Breaklub ParallelLoopState.Stop. Główna różnica polega na tym, jak szybko wszystko się psuje - Break()pętla przetworzy wszystkie elementy z wcześniejszym „indeksem” niż bieżący. Po użyciu Stop()wyjdzie tak szybko, jak to możliwe.

Aby uzyskać szczegółowe informacje, zobacz How to: Stop or Break from a Parallel.For Loop .

Reed Copsey
źródło
3
+1, wygląda na to, że kilku z nas tutaj ma dokładnie taką samą odpowiedź :) - och i mam twoją opinię na temat tego innego komentatora.
Mike Perrenoud,
Dzięki za to wyjaśnienie. Czy wiesz, kiedy wywoływane są polecenia break lub stop, czy jest tak, że aktualnie wykonywane iteracje są zakończone, czy też zatrzymuje iteracje w połowie wykonywania?
CeejeeB
1
@CeejeeB Wykonywanie operacji zakończone.
Reed Copsey
12

Powinieneś używać Anyzamiast pętli foreach:

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

Any jest na tyle sprytny, że zatrzymuje się, gdy tylko wie, że wynik musi być prawdziwy.

Servy
źródło
10

LoopState to z pewnością świetna odpowiedź. Zauważyłem, że poprzednie odpowiedzi miały tak wiele innych rzeczy, że trudno było znaleźć odpowiedź, więc oto prosty przypadek:

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
źródło
5

Po prostu użyj tego, loopStateco może być dostarczone.

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

Spójrz na przykład w tym artykule MSDN .

Mike Perrenoud
źródło