Como: Parar ou desvinculá-lo um Loop de Parallel

A exemplo a seguir mostra como quebra (ou Sair em Visual Basic) fora de um For loop e também como interromper um loop. Nesse contexto, "quebrar" significa concluir todas as iterações de todos os threads que estão antes para a iteração atual no thread atual e sair do loop. " Stop" significa parar todas as iterações assim conveniente.

Exemplo

Este exemplo demonstra uma For loop; No entanto, você pode parar ou quebrar a partir de um ForEach loop na mesma maneira. Em um ForEach um índice de iteração de loop é gerado internamente para cada elemento em cada partição.

' How to: Stop or Break from a Parallel.For Loop
Imports System.Collections.Concurrent
Imports System.Threading
Imports System.Threading.Tasks

Module ParallelForStop
    Sub Main()
        StopLoop()
        BreakAtThreshold()

        Console.WriteLine("Press any key to exit.")
        Console.ReadKey()
    End Sub

    Sub StopLoop()
        Console.WriteLine("Stop loop...")
        Dim source As Double() = MakeDemoSource(1000, 1)
        Dim results As New ConcurrentStack(Of Double)()

        ' i is the iteration variable. loopState is a 
        ' compiler-generated ParallelLoopState
        Parallel.For(0, source.Length, Sub(i, loopState)
                                           ' Take the first 100 values that are retrieved
                                           ' from anywhere in the source.
                                           If i < 100 Then
                                               ' Accessing shared object on each iteration
                                               ' is not efficient. See remarks.
                                               Dim d As Double = Compute(source(i))
                                               results.Push(d)
                                           Else
                                               loopState.[Stop]()
                                               Exit Sub

                                           End If
                                           ' Close lambda expression.
                                       End Sub)
        ' Close Parallel.For
        Console.WriteLine("Results contains {0} elements", results.Count())
    End Sub


    Sub BreakAtThreshold()
        Dim source As Double() = MakeDemoSource(10000, 1.0002)
        Dim results As New ConcurrentStack(Of Double)()

        ' Store all values below a specified threshold.
        Parallel.For(0, source.Length, Function(i, loopState)
                                           Dim d As Double = Compute(source(i))
                                           results.Push(d)
                                           If d > 0.2 Then
                                               ' Might be called more than once!
                                               loopState.Break()
                                               Console.WriteLine("Break called at iteration {0}. d = {1} ", i, d)
                                               Thread.Sleep(1000)
                                           End If
                                           Return d
                                       End Function)

        Console.WriteLine("results contains {0} elements", results.Count())
    End Sub

    Function Compute(ByVal d As Double) As Double
        'Make the processor work just a little bit.
        Return Math.Sqrt(d)
    End Function


    ' Create a contrived array of monotonically increasing
    ' values for demonstration purposes. 
    Function MakeDemoSource(ByVal size As Integer, ByVal valToFind As Double) As Double()
        Dim result As Double() = New Double(size - 1) {}
        Dim initialval As Double = 0.01
        For i As Integer = 0 To size - 1
            initialval *= valToFind
            result(i) = initialval
        Next
        Return result
    End Function
End Module
namespace StopOrBreak
{
    using System;
    using System.Collections.Concurrent;
    using System.Linq;
    using System.Threading;
    using System.Threading.Tasks;

    class Test
    {
        static void Main()
        {
            StopLoop();
            BreakAtThreshold();

            Console.WriteLine("Press any key to exit.");
            Console.ReadKey();
        }

        private static void StopLoop()
        {
            Console.WriteLine("Stop loop...");
            double[] source = MakeDemoSource(1000, 1);
            ConcurrentStack<double> results = new ConcurrentStack<double>();

            // i is the iteration variable. loopState is a 
            // compiler-generated ParallelLoopState
            Parallel.For(0, source.Length, (i, loopState) =>
            {
                // Take the first 100 values that are retrieved
                // from anywhere in the source.
                if (i < 100)
                {
                    // Accessing shared object on each iteration
                    // is not efficient. See remarks.
                    double d = Compute(source[i]);
                    results.Push(d);
                }
                else
                {
                    loopState.Stop();
                    return;
                }

            } // Close lambda expression.
            ); // Close Parallel.For

            Console.WriteLine("Results contains {0} elements", results.Count());
        }


        static void BreakAtThreshold()
        {
            double[] source = MakeDemoSource(10000, 1.0002);
            ConcurrentStack<double> results = new ConcurrentStack<double>();

            // Store all values below a specified threshold.
            Parallel.For(0, source.Length, (i, loopState) =>
            {
                double d = Compute(source[i]);
                results.Push(d);
                if (d > .2)
                {
                    // Might be called more than once!
                    loopState.Break();
                    Console.WriteLine("Break called at iteration {0}. d = {1} ", i, d);
                    Thread.Sleep(1000);
                }
            });

            Console.WriteLine("results contains {0} elements", results.Count());
        }

        static double Compute(double d)
        {
            //Make the processor work just a little bit.
            return Math.Sqrt(d);
        }


        // Create a contrived array of monotonically increasing
        // values for demonstration purposes. 
        static double[] MakeDemoSource(int size, double valToFind)
        {
            double[] result = new double[size];
            double initialval = .01;
            for (int i = 0; i < size; i++)
            {
                initialval *= valToFind;
                result[i] = initialval;
            }

            return result;
        }
    }

}

Em um ParallelFor() ou [Overload:System.Threading.Tasks.Parallel.Parallel.ForEach`1] loop, não é possível usar o mesmo quebra ou Sair instrução que é usada em um loop seqüencial, pois essas construções de linguagem são válidas para loops e "loop" paralelo é realmente um método, e não um loop. Em vez disso, você usar o Stop ou Break métodos. Algumas das sobrecargas de Parallel.For aceitar uma Action<int, ParallelLoopState> (Action(Of Integer, ParallelLoopState) em Visual Basic) como um parâmetro de entrada. O ParallelLoopState objeto é criado nos bastidores pelo tempo de execução, e você pode atribuir qualquer nome que desejar na expressão lambda.

No exemplo a seguir, o método requer apenas 100 valores da seqüência de origem e não importa quais elementos foram recuperados. Nesse caso o Stop método é usado porque informa todas as iterações do loop, incluindo aqueles começou antes para a iteração atual em outros threads, parar, assim é conveniente.

No segundo método, estamos recuperando todos os elementos para um índice especificado na seqüência de origem. Nesse caso, Break é chamado, porque quando podemos alcançar o índice em um segmento, é possível que elementos anteriores na origem ainda não foram processados. Quebra fará com que outros segmentos para abandonar o trabalho em seqments mais recente (se eles estão envolvidos em qualquer) e concluir o processamento de todos os elementos anteriores antes de sair do loop.

É importante entender que, após um Stop ou Break é chamado, outros threads em um loop podem continuar em execução durante um determinado período de tempo, o que não está no controle do desenvolvedor de aplicativo. Você pode usar o ParallelLoopState.IsStopped propriedade para verificar se o loop foi interrompido em outro segmento. No exemplo a seguir, se IsStopped for true, então nenhuma dados são gravados para a coleção.

Compilando o código

  • Copie e cole o exemplo de código em um projeto de 2010 de Visual Studio.

Consulte também

Referência

System.Action<T1, T2>

Conceitos

Paralelismo de dados (biblioteca paralela de tarefas)

Programação em paralela a.NET Framework

Expressões lambda no PLINQ e TPL