Sdílet prostřednictvím


Postupy: Zastavení nebo zrušení volaní z Parallel.For smyčky

Následující příklad ukazuje jak Break (nebo Exit v Visual Basic) ze smyčky For a také jak ukončit smyčku. V této souvislosti "break" znamená dokončení všech iterací na všech vláknech, které jsou před aktuální iterací v aktuálním vlákně a ukončení smyčky. " Stop" znamená pohodlný co nejdříve zastavit všechny iterací.

Příklad

Tento příklad ukazuje For smyčky; však ukončit nebo přerušit z ForEach smyčky v stejným způsobem. V ForEach smyčky, iterace indexu je generován interně pro každý prvek v každém oddílu.

' 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;
        }
    }

}

V ParallelFor() nebo [Overload:System.Threading.Tasks.Parallel.Parallel.ForEach`1] smyčkách, nemůžete použít stejné Break nebo Exit výrok, jaké jsou používány v sekvenčních smyčkách, protože tyto jazykové konstrukce jsou platné pro smyčky a paralelní "smyčka" je ve skutečnosti metoda, nikoli smyčka. Místo toho použijte buď Stop nebo Break metody. Některé přetížení Parallel.For přijmout Action<int, ParallelLoopState> (Action(Of Integer, ParallelLoopState) v jazyce Visual Basic) jako vstupní parametr. Objekt ParallelLoopState je vytvořen na pozadí runtime modulem a ve lambda výrazu mu můžete přiřadit libovolný název.

V následujícím příkladu metoda vyžaduje pouze 100 hodnot ze zdrojové sekvence a nezáleží na tom, které prvky byly načteny. V tomto případě je použita Stop metoda, protože zdělí všem iteracím smyčky, (i těm které začaly před aktuální iterací v jiných vláknech) aby se zastavily ihned jak to bude možné.

V druhé metodě načítáme ze zdrojové sekvence všechny elementy až k zadanému indexu. V tomto případě je zavolána metoda Break, protože když dosáhneme indexu v jednom vláknu, je možné že předchozí prvky v sekvenci ještě nebyly zpracovány. Metoda Break způsobí ukončení práce jiných vláken na pozdějších segmentech (pokud se nějakými zabývají) a dokončení zpracování všech předchozí prvků před ukončením smyčky.

Je důležité porozumět, že po tom co je zavolána buď Stop nebo Break metoda, jiná vlákna ve smyčce mohou nadále běžet po jistou dobu, kterou vývojář není schopen ovlivnit. Můžete použít vlastnost ParallelLoopState.IsStopped, k zkontrolování zda byla smyčka zastavena jiným vláknem. V následujícím příkladu, pokud je příznak IsStopped nastaven na TRUE, pak žádná další data nebudou zapsána do kolekce.

Probíhá kompilace kódu

  • Zkopírujte a vložte ukázkový kód do projekt Visual Studio 2010.

Viz také

Odkaz

System.Action<T1, T2>

Koncepty

Datový paralelismus (Task Parallel Library)

Paralelní programování v rozhraní .NET Framework

Lambda výrazy v PLINQ a TPL