Rompre le parallèle pour chacun?

111

Comment sortir d'une boucle parallel.for ?

J'ai une déclaration assez complexe qui ressemble à ce qui suit:

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

En utilisant la classe parallèle, je peux optimiser ce processus de loin. Toutefois; Je n'arrive pas à comprendre comment briser la boucle parallèle? L' break;instruction renvoie l'erreur de syntaxe suivante:

Pas de boucles englobantes pour interrompre ou continuer

Rasmus Søborg
la source
1
Vous attendez-vous à ce que TOUTES les instances parallèles de la boucle rompent en même temps?
n8wrl

Réponses:

185

Utilisez la ParallelLoopState.Breakméthode:

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

Ou dans votre cas:

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
la source
exactement. était sur le point de publier cela moi-même.
Mare Infinitus
1
En pensant à une boucle foreach séquentielle, il est garanti que les éléments avant l'élément qui, pour une raison quelconque, ont causé la rupture sont traités. Qu'en est-il d'un Parallel.ForEach où l'ordre des articles ne doit pas nécessairement être l'ordre dans lequel ils sont traités? Est-il également garanti que tous les éléments d'un IEnumerable <...> avant celui qui invoque state.Break () sont en cours de traitement et ceux qui viennent après ne le sont pas? Bien que le premier puisse être réalisé d'une manière ou d'une autre, je ne vois pas du tout comment le second serait possible.
Hendrik Wiese
4
@Hendrik Wiese: Les documents disent: 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.etthere is no guarantee that iterations after the current one will definitely not execute.
Tudor
2
alors serait state.Stop()plus approprié pour atteindre de manière fiable les résultats attendus, comme mentionné ci-dessous par Mike Perrenoud et MBentley
xtreampb
44

Vous faites cela en appelant en utilisant la surcharge de Parallel.Forou Parallel.ForEachqui passe dans un état de boucle, puis en appelant ParallelLoopState.Breakou ParallelLoopState.Stop. La principale différence réside dans la rapidité avec laquelle les choses se cassent - avec Break(), la boucle traitera tous les éléments avec un "index" antérieur à l'actuel. Avec Stop(), il sortira le plus rapidement possible.

Pour plus de détails, consultez Comment: arrêter ou interrompre une boucle Parallel.For .

Reed Copsey
la source
3
+1, il semble que quelques-uns d'entre nous ici ont exactement la même réponse :) - oh et j'ai votre dos là-haut sur cet autre homme de commentaire.
Mike Perrenoud
Merci pour cette explication. Savez-vous que lorsque break ou stop sont appelés, est-ce que les itérations en cours d'exécution sont terminées ou est-ce que cela arrête les itérations au milieu de l'exécution?
CeejeeB
1
@CeejeeB Les opérations en cours d'exécution sont terminées.
Reed Copsey
12

Ce que vous devriez utiliser est Anyplutôt qu'une boucle foreach:

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

Any est assez intelligent pour s'arrêter dès qu'il sait que le résultat doit être vrai.

Servy
la source
10

LoopState est certainement une excellente réponse. J'ai trouvé que les réponses précédentes contenaient tellement d'autres choses qu'il était difficile de voir la réponse, alors voici un cas simple:

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
la source
5

Utilisez simplement le loopStatequi peut être fourni.

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

Regardez cet article MSDN pour un exemple.

Mike Perrenoud
la source