다음을 통해 공유


BlockingCollection 개요

BlockingCollection<T> 는 다음 기능을 제공하는 스레드로부터 안전한 컬렉션 클래스입니다.

  • Producer-Consumer 패턴의 구현입니다.

  • 여러 스레드에서 항목을 동시에 추가하고 가져옵니다.

  • 선택적 최대 용량입니다.

  • 컬렉션이 비어 있거나 꽉 찼을 때 블로킹되는 삽입 및 제거 연산입니다.

  • 지정한 기간까지 차단하거나 차단하지 않는 "try" 작업을 삽입 및 제거합니다.

  • IProducerConsumerCollection<T>을(를) 구현하는 모든 컬렉션 형식을 캡슐화합니다.

  • 취소 토큰을 사용한 취소

  • (Visual Basic에서)foreach를 사용하는 두 종류의 열거형 For Each :

    1. 읽기 전용 열거형입니다.

    2. 항목이 열거될 때 항목을 제거하는 열거형입니다.

경계 및 차단 지원

BlockingCollection<T> 는 경계 및 차단을 지원합니다. "바운딩은 컬렉션의 최대 용량을 설정하는 것을 의미합니다." 메모리에서 컬렉션의 최대 크기를 제어하고 생성 스레드가 소비 스레드보다 너무 앞서 가지 않도록 하기 때문에, 특정 시나리오에서 바운딩이 중요합니다.

여러 스레드 또는 태스크가 동시에 컬렉션에 항목을 추가할 수 있으며 컬렉션이 지정된 최대 용량에 도달하면 항목이 제거될 때까지 생성 스레드가 차단됩니다. 여러 소비자가 동시에 항목을 제거할 수 있으며 컬렉션이 비어 있으면 생산자가 항목을 추가할 때까지 소비 스레드가 차단됩니다. 생성 스레드는 더 이상 항목이 추가되지 않음을 나타내기 위해 호출 CompleteAdding 할 수 있습니다. 소비자는 컬렉션이 IsCompleted 비어 있고 더 이상 항목이 추가되지 않는 시기를 알 수 있도록 속성을 모니터링합니다. 다음 예제에서는 제한된 용량이 100인 간단한 BlockingCollection을 보여줍니다. 생산자 작업은 일부 외부 조건이 true인 동안 컬렉션에 항목을 추가하고, 그런 다음 CompleteAdding을 호출합니다. 소비자 작업은 IsCompleted 속성이 true가 될 때까지 아이템을 가져옵니다.

// A bounded collection. It can hold no more
// than 100 items at once.
BlockingCollection<Data> dataItems = new BlockingCollection<Data>(100);

// A simple blocking consumer with no cancellation.
Task.Run(() =>
{
    while (!dataItems.IsCompleted)
    {

        Data data = null;
        // Blocks if dataItems.Count == 0.
        // IOE means that Take() was called on a completed collection.
        // Some other thread can call CompleteAdding after we pass the
        // IsCompleted check but before we call Take.
        // In this example, we can simply catch the exception since the
        // loop will break on the next iteration.
        try
        {
            data = dataItems.Take();
        }
        catch (InvalidOperationException) { }

        if (data != null)
        {
            Process(data);
        }
    }
    Console.WriteLine("\r\nNo more items to take.");
});

// A simple blocking producer with no cancellation.
Task.Run(() =>
{
    while (moreItemsToAdd)
    {
        Data data = GetData();
        // Blocks if numbers.Count == dataItems.BoundedCapacity
        dataItems.Add(data);
    }
    // Let consumer know we are done.
    dataItems.CompleteAdding();
});

' A bounded collection. It can hold no more 
' than 100 items at once.
Dim dataItems = New BlockingCollection(Of Data)(100)

' A simple blocking consumer with no cancellation.
Task.Factory.StartNew(Sub()
                          While dataItems.IsCompleted = False
                              Dim dataItem As Data = Nothing
                              Try
                                  dataItem = dataItems.Take()
                              Catch e As InvalidOperationException
                                  ' IOE means that Take() was called on a completed collection.
                                  ' In this example, we can simply catch the exception since the 
                                  ' loop will break on the next iteration.
                              End Try
                              If (dataItem IsNot Nothing) Then
                                  Process(dataItem)
                              End If
                          End While
                          Console.WriteLine(vbCrLf & "No more items to take.")
                      End Sub)

' A simple blocking producer with no cancellation.
Task.Factory.StartNew(Sub()
                          While moreItemsToAdd = True
                              Dim item As Data = GetData()

                              ' Blocks if dataItems.Count = dataItems.BoundedCapacity.
                              dataItems.Add(item)
                          End While

                          ' Let consumer know we are done.
                          dataItems.CompleteAdding()
                      End Sub)

전체 예제는 방법: BlockingCollection에서 개별적으로 항목 추가 및 가져오기를 참조하세요.

시간 제한 차단 작업

제한된 컬렉션에 대한 시간 제한 차단 TryAddTryTake 작업에서 메서드는 항목을 추가하거나 가져오려고 시도합니다. 항목을 사용할 수 있는 경우 참조로 전달된 변수에 배치되고 메서드는 true를 반환합니다. 지정된 제한 시간 이후에 항목을 검색하지 않으면 메서드가 false를 반환합니다. 그런 다음 스레드는 컬렉션에 다시 액세스하기 전에 다른 유용한 작업을 자유롭게 수행할 수 있습니다. 시간 제한 차단 액세스의 예는 방법: BlockingCollection에서 개별적으로 항목 추가 및 가져오기의 두 번째 예제를 참조하세요.

추가 및 실행 작업 취소

추가 및 테이크 작업은 일반적으로 루프에서 수행됩니다. 또는 CancellationToken 메서드에 전달한 다음 각 반복에서 TryAddTryTake 토큰 IsCancellationRequested 의 속성 값을 확인하여 루프를 취소할 수 있습니다. 값이 true이면 리소스를 정리하고 루프를 종료하여 취소 요청에 응답해야 합니다. 다음 예제는 취소 토큰을 포함하는 오버로드 TryAdd와 이를 사용하는 코드를 보여줍니다.

do
{
    // Cancellation causes OCE. We know how to handle it.
    try
    {
        success = bc.TryAdd(itemToAdd, 2, ct);
    }
    catch (OperationCanceledException)
    {
        bc.CompleteAdding();
        break;
    }
    //...
} while (moreItems == true);
Do While moreItems = True
    ' Cancellation causes OCE. We know how to handle it.
    Try
        success = bc.TryAdd(itemToAdd, 2, ct)
    Catch ex As OperationCanceledException
        bc.CompleteAdding()
        Exit Do
    End Try
Loop

취소 지원을 추가하는 방법에 대한 예제는 방법: BlockingCollection에서 개별적으로 항목 추가 및 가져오기의 두 번째 예제를 참조하세요.

컬렉션 형식 지정

만들 BlockingCollection<T>때 제한된 용량뿐만 아니라 사용할 컬렉션 유형도 지정할 수 있습니다. 예를 들어 FIFO(먼저 들어온 것이 먼저 나가는 것) 동작을 지정할 ConcurrentQueue<T> 수 있거나, LIFO(나중에 들어온 것이 먼저 나가는 것) 동작을 지정할 ConcurrentStack<T> 수 있습니다. IProducerConsumerCollection<T> 인터페이스를 구현하는 모든 컬렉션 클래스를 사용할 수 있습니다. 기본 컬렉션 유형은 BlockingCollection<T>이며, ConcurrentQueue<T>입니다. 다음 코드 예제는 용량이 1000인 문자열 BlockingCollection<T>를 생성하고 ConcurrentBag<T>을 사용하는 방법을 보여줍니다.

Dim bc = New BlockingCollection(Of String)(New ConcurrentBag(Of String()), 1000)  
BlockingCollection<string> bc = new BlockingCollection<string>(new ConcurrentBag<string>(), 1000 );  

자세한 내용은 방법: 컬렉션에 경계 및 차단 기능 추가를 참조하세요.

IEnumerable 지원

BlockingCollection<T> GetConsumingEnumerable 메서드는 소비자가 foreach를 (For Each Visual Basic에서) 사용하여 컬렉션이 완료될 때까지, 즉 컬렉션이 비어 있고 더 이상 항목이 추가되지 않을 때까지 항목을 제거할 수 있도록 합니다. 자세한 내용은 방법: ForEach를 사용하여 BlockingCollection에서 항목 제거를 참조하세요.

여러 BlockingCollections를 하나로 사용

소비자가 여러 컬렉션의 항목을 동시에 가져와야 하는 시나리오의 경우, BlockingCollection<T>의 배열을 생성하고, 이 배열 내 컬렉션에서 항목을 추가하거나 가져오는 TakeFromAny, AddToAny 같은 정적 메서드를 사용할 수 있습니다. 한 컬렉션이 차단되는 경우 메서드는 작업을 수행할 수 있는 컬렉션을 찾을 때까지 즉시 다른 컬렉션을 시도합니다. 자세한 내용은 방법: 파이프라인에서 차단 컬렉션 배열을 사용합니다.

참고하십시오