Per visualizzare l'articolo in inglese, selezionare la casella di controllo Inglese. È possibile anche visualizzare il testo inglese in una finestra popup posizionando il puntatore del mouse sopra il testo.
Traduzione
Inglese
We recommend using Visual Studio 2017

Procedura: arrestare o interrompere un ciclo Parallel.For

Nell'esempio seguente viene mostrato come uscire da un ciclo For mediante l'istruzione break (o Exit in Visual Basic) nonché come arrestare un ciclo. In questo contesto, "interrompere" significa completare tutte le iterazioni in tutti i thread precedenti all'iterazione corrente nel thread corrente e quindi uscire dal ciclo. "Arrestare" significa invece arrestare tutte le iterazioni non appena risulta appropriato.

In questo esempio viene illustrato un ciclo For; è comunque possibile arrestare o interrompere un ciclo ForEach nello stesso modo. In un ciclo ForEach, viene generato internamente un indice di iterazione per ogni elemento in ogni partizione.


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


In un ciclo Parallel.For o Parallel.ForEach non è possibile utilizzare la stessa istruzione break o Exit utilizzata in un ciclo sequenziale poiché questi costrutti di linguaggio sono validi per i cicli, e un "ciclo" parallelo in realtà è un metodo, non un ciclo. Al posto di queste istruzioni si utilizza invece il metodo Stop o Break. Alcuni degli overload di Parallel.For accettano un oggetto Action<int, ParallelLoopState> (Action(Of Integer, ParallelLoopState) in Visual Basic) come parametro di input. L'oggetto ParallelLoopState viene creato automaticamente dal runtime ed è possibile assegnarvi il nome desiderato nell'espressione lambda.

Nell'esempio seguente il metodo StopLoop()richiede solo 100 valori della sequenza di origine senza dare importanza a quali elementi vengono recuperati. In questo caso si utilizza il metodo Stop poiché indica a tutte le iterazioni del ciclo, comprese quelle iniziate prima dell'iterazione corrente in altri thread, di arrestarsi non appena risulta appropriato.

Nel metodo BreakAtThreshold() si recuperano tutti gli elementi della sequenza di origine dal primo fino a raggiungere un indice specificato. In questo caso si chiama Break, poiché quando si raggiunge l'indice in un determinato thread è possibile che alcuni elementi precedenti dell'origine non siano stati ancora elaborati. Break causa l'interruzione delle operazioni dei thread sui segmenti successivi (se presenti) e il completamento dell'elaborazione di tutti gli elementi precedenti prima di chiudere il ciclo.

Si noti che non è possibile controllare se gli altri thread in un ciclo continuano a essere eseguiti dopo che viene chiamato Stop o Break. È possibile utilizzare la proprietà ParallelLoopState.IsStopped per controllare se il ciclo è stato arrestato in un altro thread.

  • Copiare e incollare l'esempio di codice in un progetto di Visual Studio.

Mostra: