다음을 통해 공유


카운트다운 이벤트

System.Threading.CountdownEvent 특정 횟수의 신호를 받은 후 대기 중인 스레드의 차단을 해제하는 동기화 기본 형식입니다. CountdownEvent은 이벤트를 알리기 전에 변수를 수동으로 감소시켜야 하는 경우에 ManualResetEvent 또는 ManualResetEventSlim를 사용하는 대신 설계된 시나리오를 위해 만들어졌습니다. 예를 들어 포크/조인 시나리오에서는 신호 수가 5인 CountdownEvent 만든 다음 스레드 풀에서 5개의 작업 항목을 시작하고 완료 시 각 작업 항목이 Signal 호출하도록 할 수 있습니다. Signal 호출할 때마다 신호 수가 1씩 감소합니다. 주 스레드에서 Wait 호출은 신호 수가 0이 될 때까지 차단됩니다.

비고

레거시 .NET Framework 동기화 API와 상호 작용할 필요가 없는 코드의 경우 포크 조인 병렬 처리를 보다 쉽게 표현할 수 있도록 System.Threading.Tasks.Task 개체 또는 Invoke 메서드를 사용하는 것이 좋습니다.

CountdownEvent 다음과 같은 추가 기능이 있습니다.

  • 취소 토큰을 사용하여 대기 작업을 취소할 수 있습니다.

  • 인스턴스를 만든 후 신호 수를 증분할 수 있습니다.

  • Wait 메서드를 호출하여 Reset 반환된 후 인스턴스를 다시 사용할 수 있습니다.

  • 인스턴스는 WaitHandle같은 다른 .NET 동기화 API와 통합하기 위한 WaitAll 노출합니다.

기본 사용량

다음 예제에서는 CountdownEventThreadPool 작업 항목과 함께 사용하는 방법을 보여 줍니다.

IEnumerable<Data> source = GetData();
using (CountdownEvent e = new CountdownEvent(1))
{
    // fork work:
    foreach (Data element in source)
    {
        // Dynamically increment signal count.
        e.AddCount();
        ThreadPool.QueueUserWorkItem(delegate(object state)
         {
             try
             {
                 ProcessData(state);
             }
             finally
             {
                 e.Signal();
             }
         },
         element);
    }
    e.Signal();

    // The first element could be run on this thread.

    // Join with work.
    e.Wait();
}
// .,.
Dim source As IEnumerable(Of Data) = GetData()
Dim e = New CountdownEvent(1)

' Fork work:
For Each element As Data In source
    ' Dynamically increment signal count.
    e.AddCount()

    ThreadPool.QueueUserWorkItem(Sub(state)
                                     Try
                                         ProcessData(state)
                                     Finally
                                         e.Signal()
                                     End Try
                                 End Sub,
                                  element)
Next
' Decrement the signal count by the one we added
' in the constructor.
e.Signal()

' The first element could also be run on this thread.
' ProcessData(New Data(0))

' Join with work:
e.Wait()

취소 기능이 있는 CountdownEvent

다음 예제에서는 취소 토큰을 사용하여 CountdownEvent 대기 작업을 취소하는 방법을 보여줍니다. 기본 패턴은 .NET Framework 4에서 도입된 통합 취소에 대한 모델을 따릅니다. 자세한 내용은 관리되는 스레드에서 취소을 참조하세요.

class CancelableCountdownEvent
{
    class Data
    {
        public int Num { get; set; }
        public Data(int i) { Num = i; }
        public Data() { }
    }

    class DataWithToken
    {
        public CancellationToken Token { get; set; }
        public Data Data { get; private set; }
        public DataWithToken(Data data, CancellationToken ct)
        {
            this.Data = data;
            this.Token = ct;
        }
    }
    static IEnumerable<Data> GetData()
    {
        return new List<Data>() { new Data(1), new Data(2), new Data(3), new Data(4), new Data(5) };
    }
    static void ProcessData(object obj)
    {
        DataWithToken dataWithToken = (DataWithToken)obj;
        if (dataWithToken.Token.IsCancellationRequested)
        {
            Console.WriteLine($"Canceled before starting {dataWithToken.Data.Num}");
            return;
        }

        for (int i = 0; i < 10000; i++)
        {
            if (dataWithToken.Token.IsCancellationRequested)
            {
                Console.WriteLine($"Cancelling while executing {dataWithToken.Data.Num}");
                return;
            }
            // Increase this value to slow down the program.
            Thread.SpinWait(100000);
        }
        Console.WriteLine($"Processed {dataWithToken.Data.Num}");
    }

    static void Main(string[] args)
    {
        EventWithCancel();

        Console.WriteLine("Press enter to exit.");
        Console.ReadLine();
    }

    static void EventWithCancel()
    {
        IEnumerable<Data> source = GetData();
        CancellationTokenSource cts = new CancellationTokenSource();

        //Enable cancellation request from a simple UI thread.
        Task.Factory.StartNew(() =>
             {
                 if (Console.ReadKey().KeyChar == 'c')
                     cts.Cancel();
             });

        // Event must have a count of at least 1
        CountdownEvent e = new CountdownEvent(1);

        // fork work:
        foreach (Data element in source)
        {
            DataWithToken item = new DataWithToken(element, cts.Token);
            // Dynamically increment signal count.
            e.AddCount();
            ThreadPool.QueueUserWorkItem(delegate(object state)
             {
                 ProcessData(state);
                 if (!cts.Token.IsCancellationRequested)
                     e.Signal();
             },
             item);
        }
        // Decrement the signal count by the one we added
        // in the constructor.
        e.Signal();

        // The first element could be run on this thread.

        // Join with work or catch cancellation.
        try
        {
            e.Wait(cts.Token);
        }
        catch (OperationCanceledException oce)
        {
            if (oce.CancellationToken == cts.Token)
            {
                Console.WriteLine("User canceled.");
            }
            else
            {
                throw; //We don't know who canceled us!
            }
        }
        finally {
            e.Dispose();
            cts.Dispose();
        }
        //...
    } //end method
} //end class
Option Strict On
Option Explicit On

Imports System.Collections
Imports System.Collections.Generic
Imports System.Linq
Imports System.Threading
Imports System.Threading.Tasks

Module CancelEventWait

    Class Data
        Public Num As Integer
        Public Sub New(ByVal i As Integer)
            Num = i
        End Sub
        Public Sub New()

        End Sub
    End Class

    Class DataWithToken
        Public Token As CancellationToken
        Public _data As Data
        Public Sub New(ByVal d As Data, ByVal ct As CancellationToken)
            Me._data = d
            Me.Token = ct
        End Sub
    End Class

    Class Program
        Shared Function GetData() As IEnumerable(Of Data)
            Dim nums = New List(Of Data)
            For i As Integer = 1 To 5
                nums.Add(New Data(i))
            Next
            Return nums
        End Function

        Shared Sub ProcessData(ByVal obj As Object)
            Dim dataItem As DataWithToken = CType(obj, DataWithToken)
            If dataItem.Token.IsCancellationRequested = True Then
                Console.WriteLine("Canceled before starting {0}", dataItem._data.Num)
                Exit Sub
            End If

            ' Increase this value to slow down the program.
            For i As Integer = 0 To 10000

                If dataItem.Token.IsCancellationRequested = True Then
                    Console.WriteLine("Cancelling while executing {0}", dataItem._data.Num)
                    Exit Sub
                End If
                Thread.SpinWait(100000)
            Next
            Console.WriteLine("Processed {0}", dataItem._data.Num)


        End Sub

        Shared Sub Main()
            DoEventWithCancel()
            Console.WriteLine("Press the enter key to exit.")
            Console.ReadLine()
        End Sub

        Shared Sub DoEventWithCancel()
            Dim source As IEnumerable(Of Data) = GetData()
            Dim cts As CancellationTokenSource = New CancellationTokenSource()

            ' Enable cancellation request from a simple UI thread.
            Task.Factory.StartNew(Sub()
                                      If Console.ReadKey().KeyChar = "c"c Then
                                          cts.Cancel()
                                      End If
                                  End Sub)

            ' Must have a count of at least 1 or else it is signaled.
            Dim e As CountdownEvent = New CountdownEvent(1)

            For Each element As Data In source
                Dim item As DataWithToken = New DataWithToken(element, cts.Token)

                ' Dynamically increment signal count.
                e.AddCount()

                ThreadPool.QueueUserWorkItem(Sub(state)
                                                 ProcessData(state)
                                                 If cts.Token.IsCancellationRequested = False Then
                                                     e.Signal()
                                                 End If
                                             End Sub,
                                            item)
            Next
            ' Decrement the signal count by the one we added
            ' in the constructor.
            e.Signal()
            ' The first element could be run on this thread.
            ' ProcessData(source(0))

            ' Join with work or catch cancellation exception
            Try
                e.Wait(cts.Token)
            Catch ex As OperationCanceledException
                If ex.CancellationToken = cts.Token Then
                    Console.WriteLine("User canceled.")
                Else : Throw ' we don't know who canceled us.

                End If
            Finally
                e.Dispose()
                cts.Dispose()
            End Try
        End Sub
    End Class
End Module

대기 작업은 신호를 전송하는 스레드를 취소하지 않습니다. 일반적으로 취소는 논리 작업에 적용되며 대기가 동기화되는 모든 작업 항목뿐만 아니라 이벤트에 대한 대기를 포함할 수 있습니다. 이 예제에서는 각 작업 항목이 취소 요청에 응답할 수 있도록 동일한 취소 토큰의 복사본을 전달합니다.

참고하십시오