Quebrar parallel.foreach?

111

Como faço para sair de um loop parallel.for ?

Eu tenho uma declaração bastante complexa que se parece com o seguinte:

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

Usando a classe paralela, posso otimizar esse processo de longe. Contudo; Não consigo descobrir como quebrar o loop paralelo? A break;instrução gera o seguinte erro de sintaxe:

Sem loops delimitadores dos quais quebrar ou continuar

Rasmus Søborg
fonte
1
Você esperaria que TODAS as instâncias paralelas do loop fossem interrompidas ao mesmo tempo?
n8wrl

Respostas:

185

Use o ParallelLoopState.Breakmétodo:

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

Ou no seu caso:

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
fonte
exatamente. estava prestes a postar isso sozinho.
Mare Infinitus
1
Pensando em um loop foreach sequencial, é garantido que os itens antes do item que por qualquer motivo causou a quebra sejam processados. Que tal um Parallel.ForEach em que a ordem dos itens não precisa necessariamente ser a ordem em que são processados? Também é garantido que todos os itens em um IEnumerable <...> antes daquele que invoca state.Break () estão sendo processados ​​e os que vêm depois dele não estão? Embora o primeiro pudesse ser alcançado de alguma forma, não vejo como o último seria possível.
Hendrik Wiese
4
@Hendrik Wiese: Documentos dizem: 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.ethere is no guarantee that iterations after the current one will definitely not execute.
Tudor
2
então seria state.Stop()mais apropriado atingir os resultados esperados de forma confiável, conforme mencionado abaixo por Mike Perrenoud e MBentley
xtreampb
44

Você faz isso chamando usando a sobrecarga de Parallel.Forou Parallel.ForEachque passa em um estado de loop e chamando ParallelLoopState.Breakou ParallelLoopState.Stop. A principal diferença é a rapidez com que as coisas são interrompidas - com Break()o loop processará todos os itens com um "índice" anterior ao atual. Com Stop(), ele sairá o mais rápido possível.

Para obter detalhes, consulte How to: Stop or Break from a Parallel.For Loop .

Reed Copsey
fonte
3
+1, parece que alguns de nós aqui temos exatamente a mesma resposta :) - oh e eu tenho sua opinião sobre aquele outro comentário cara.
Mike Perrenoud
Obrigado por esta explicação. Você sabe quando break ou stop são chamados, é o caso de que as iterações atualmente em execução são concluídas ou ele para as iterações no meio da execução?
CeejeeB
1
@CeejeeB As operações em execução no momento foram concluídas.
Reed Copsey
12

O que você deve usar é Any, em vez de um loop foreach:

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

Any é inteligente o suficiente para parar assim que souber que o resultado deve ser verdadeiro.

Servy
fonte
10

LoopState é certamente uma ótima resposta. Descobri que as respostas anteriores tinham tantas outras coisas que era difícil ver a resposta, então aqui está um caso simples:

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
fonte
5

Basta usar o loopStateque pode ser fornecido.

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

Veja um exemplo neste artigo do MSDN .

Mike Perrenoud
fonte