Share via


방법: Parallel.For 루프에서 중지 또는 중단

다음 예제에서는 For 루프에서 break 문(Visual Basic의 경우 Exit 문)을 실행하는 방법과 루프를 중지하는 방법을 보여 줍니다. 여기서 "중단"(break)은 현재 스레드의 현재 반복 이전에 실행된 모든 스레드에서의 모든 반복을 완료한 다음 루프를 종료하는 것을 의미합니다. 이때 중지"란 가능한 한 즉시 모든 반복을 중지하는 것을 의미합니다.

예제

이 예제는 For 반복을 보여 줍니다. 그러나 ForEach 반복에서도 이와 동일하게 반복을 중지하거나 중단할 수 있습니다. ForEach 반복에서 반복 인덱스는 각 분할의 각 요소에 대해 내부적으로 생성됩니다.

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

}

ParallelFor() 또는 [Overload:System.Threading.Tasks.Parallel.Parallel.ForEach`1] 루프에서는 순차 루프에 사용되는 것과 동일한 break 또는 Exit 문을 사용할 수 없습니다. 이러한 언어 구문은 루프에 사용해야 하며 병렬 "루프"는 실제로는 루프가 아니라 메서드이기 때문입니다. 따라서 Stop 또는 Break 메서드를 대신 사용합니다. Parallel.For의 일부 오버로드에는 입력 매개 변수로 Action<int, ParallelLoopState>(Visual Basic의 경우 Action(Of Integer, ParallelLoopState))를 사용할 수 있습니다. ParallelLoopState 개체는 런타임에 의해 자동으로 만들어지며 람다 식에서 이 개체에 원하는 이름을 지정할 수 있습니다.

다음 예제의 메서드에는 소스 시퀀스의 값이 100개만 필요하며 어떤 요소가 검색되었는지는 중요하지 않습니다. Stop 메서드는 현재 반복 이전에 시작된 다른 스레드의 반복을 포함하여 루프의 모든 반복이 가능한 한 즉시 중지되도록 하므로 이 경우에는 이 메서드가 사용됩니다.

두 번째 메서드에서는 소스 시퀀스에서 지정된 인덱스까지의 모든 요소를 검색합니다. 이 경우 한 스레드에서 해당 인덱스에 도달할 때 소스의 이전 요소가 아직 처리되지 않았을 수 있으므로 Break가 호출됩니다. Break를 사용하면 다른 스레드에서는 이후 세그먼트(관련된 경우)에 대한 작업을 포기하고 이전의 모든 요소에 대한 처리를 완료한 다음 루프를 종료하게 됩니다.

Stop 또는 Break가 호출된 후에도 얼마 동안은 루프의 다른 스레드가 계속 실행되고 있을 수 있으며 이는 응용 프로그램 개발자가 제어할 수 없다는 것을 이해하고 있어야 합니다. ParallelLoopState.IsStopped 속성을 사용하여 다른 스레드에서 루프가 중지되었는지 여부를 확인할 수 있습니다. 다음 예제에서는 IsStopped가 true인 경우 컬렉션에 더 이상 데이터를 쓰지 않습니다.

코드 컴파일

  • 이 코드 예제를 복사하여 Visual Studio 2010 프로젝트에 붙여넣습니다.

참고 항목

참조

System.Action<T1, T2>

개념

데이터 병렬 처리(작업 병렬 라이브러리)

.NET Framework의 병렬 프로그래밍

PLINQ 및 TPL의 람다 식