共用方式為


HOW TO:從 BlockingCollection 個別加入和擷取項目

這個範例顯示如何利用封鎖方式和非封鎖方式在 BlockingCollection<T> 中加入和移除項目。 如需 BlockingCollection<T> 的詳細資訊,請參閱 BlockingCollection 概觀

如需如何列舉 BlockingCollection<T> 直到其為空白且不再加入新元素的範例,請參閱 HOW TO:使用 ForEach 來移除 BlockingCollection 中的項目

範例

這個範例顯示如何加入和擷取項目,以便在集合暫時空白 (擷取時)、達到最大容量 (加入時) 或超過指定的逾時期限時封鎖作業。 請注意,只有在已建立 BlockingCollection,且其具有建構函式中指定的最大容量時,才會啟用最大容量封鎖。

Option Strict On
Option Explicit On

Imports System.Collections.Concurrent
Imports System.Threading
Imports System.Threading.Tasks

Module SimpleBlocking

    Class Program
        Shared Sub Main()
            ' Increase or decrease this value as desired.
            Dim itemsToAdd As Integer = 500

            ' Preserve all the display output for Adds and Takes
            Console.SetBufferSize(80, (itemsToAdd * 2) + 3)

            ' A bounded collection. Increase, decrease, or remove the 
            ' maximum capacity argument to see how it impacts behavior.
            Dim numbers = New BlockingCollection(Of Integer)(50)

            ' A simple blocking consumer with no cancellation.
            Task.Factory.StartNew(Sub()
                                      Dim i As Integer = -1
                                      While numbers.IsCompleted = False
                                          Try
                                              i = numbers.Take()
                                          Catch ioe As InvalidOperationException
                                              Console.WriteLine("Adding was completed!")
                                              Exit While
                                          End Try
                                          Console.WriteLine("Take:{0} ", i)
                                          ' Simulate a slow consumer. This will cause
                                          ' collection to fill up fast and thus Adds wil block.
                                          Thread.SpinWait(100000)
                                      End While
                                      Console.WriteLine(vbCrLf & "No more items to take. Press the Enter key to exit.")
                                  End Sub)

            ' A simple blocking producer with no cancellation.
            Task.Factory.StartNew(Sub()
                                      For i As Integer = 0 To itemsToAdd
                                          numbers.Add(i)
                                          Console.WriteLine("Add:{0} Count={1}", i, numbers.Count)
                                      Next

                                      'See documentation for this method.
                                      numbers.CompleteAdding()
                                  End Sub)

            'Keep the console window open in debug mode.
            Console.ReadLine()
        End Sub
    End Class

End Module
namespace BCBlockingAccess
{
    using System;
    using System.Collections.Concurrent;


    class Program
    {
        static void Main(string[] args)
        {
            // Increase or decrease this value as desired.
            int itemsToAdd = 500;

            // Preserve all the display output for Adds and Takes
            Console.SetBufferSize(80, (itemsToAdd * 2) + 3);

            // A bounded collection. Increase, decrease, or remove the 
            // maximum capacity argument to see how it impacts behavior.
            BlockingCollection<int> numbers = new BlockingCollection<int>(50);


            // A simple blocking consumer with no cancellation.
            Task.Factory.StartNew(() =>
            {
                int i = -1;
                while (!numbers.IsCompleted)
                {
                    try
                    {
                        i = numbers.Take();
                    }
                    catch (InvalidOperationException)
                    {
                        Console.WriteLine("Adding was compeleted!");
                        break;
                    }
                    Console.WriteLine("Take:{0} ", i);

                    // Simulate a slow consumer. This will cause
                    // collection to fill up fast and thus Adds wil block.
                    Thread.SpinWait(100000);
                }

                Console.WriteLine("\r\nNo more items to take. Press the Enter key to exit.");
            });

            // A simple blocking producer with no cancellation.
            Task.Factory.StartNew(() =>
            {
                for (int i = 0; i < itemsToAdd; i++)
                {
                    numbers.Add(i);
                    Console.WriteLine("Add:{0} Count={1}", i, numbers.Count);
                }

                // See documentation for this method.
                numbers.CompleteAdding();
            });

            // Keep the console display open in debug mode.

            Console.ReadLine();
        }
    }
}

第二個範例顯示如何加入和擷取項目以便不讓作業受到封鎖。 如果不存在任何項目、已達到界限集合的最大容量或者超過逾時期限,則 TryAdd() 或 TryTake() 作業會傳回 false。 如此便可讓執行緒在一段時間內先執行其他部分有用的工作,然後重新嘗試擷取新的項目,或者嘗試加入之前無法加入的相同項目。 該程式也會示範如何在存取 BlockingCollection<T> 時實作取消作業。

Option Strict On
Option Explicit On
Imports System.Collections.Concurrent
Imports System.Threading
Imports System.Threading.Tasks
Module NonBlockingBC


    Class NonBlockingAccess
        Shared inputs As Integer
        Shared Sub Main()
            ' The token source for issuing the cancelation request.
            Dim cts As CancellationTokenSource = New CancellationTokenSource()

            ' A blocking collection that can hold no more than 100 items at a time.
            Dim numberCollection As BlockingCollection(Of Integer) = New BlockingCollection(Of Integer)(100)

            ' Set console buffer to hold our prodigious output.
            Console.SetBufferSize(80, 2000)

            ' The simplest UI thread ever invented.
            Task.Factory.StartNew(Sub()
                                      If Console.ReadKey.KeyChar() = "c"c Then
                                          cts.Cancel()
                                      End If
                                  End Sub)
            ' Start one producer and one consumer.
            Task.Factory.StartNew(Sub() NonBlockingConsumer(numberCollection, cts.Token))
            Task.Factory.StartNew(Sub() NonBlockingProducer(numberCollection, cts.Token))


            Console.WriteLine("Press the Enter key to exit.")
            Console.ReadLine()
        End Sub

        Shared Sub NonBlockingConsumer(ByVal bc As BlockingCollection(Of Integer), ByVal ct As CancellationToken)

            ' IsCompleted is equivalent to (IsAddingCompleted And Count = 0)
            While bc.IsCompleted = False
                Dim nextItem As Integer = 0
                Try
                    If bc.TryTake(nextItem, 0, ct) Then
                        Console.WriteLine("  Take Blocked.")
                    Else
                        Console.WriteLine(" Take: {0}", nextItem)
                    End If
                Catch ex As OperationCanceledException
                    Console.WriteLine("Taking canceled.")
                    Exit While
                End Try
                'Slow down consumer just a little to cause
                ' collection to fill up faster, and lead to "AddBlocked"
                Thread.SpinWait(500000)
            End While

            Console.WriteLine(vbCrLf & "No more items to take. Press the Enter key to exit.")
        End Sub

        Shared Sub NonBlockingProducer(ByVal bc As BlockingCollection(Of Integer), ByVal ct As CancellationToken)
            Dim itemToAdd As Integer = 0
            Dim success As Boolean = False

            Do While itemToAdd < inputs
                'Cancellation causes OCE. We know how to handle it.
                Try
                    success = bc.TryAdd(itemToAdd, 2, ct)
                Catch ex As OperationCanceledException
                    Console.WriteLine("Add loop canceled.")

                    ' Let other threads know we're done in case
                    ' they aren't monitoring the cancellation token.
                    bc.CompleteAdding()
                    Exit Do
                End Try

                If success = True Then
                    Console.WriteLine(" Add:{0}", itemToAdd)
                    itemToAdd = itemToAdd + 1
                Else
                    Console.Write("  AddBlocked:{0} Count = {1}", itemToAdd.ToString(), bc.Count)

                    ' Don't increment nextItem. Try again on next iteration
                    ' Do something else useful instead.
                    UpdateProgress(itemToAdd)
                End If
            Loop
        End Sub

        Shared Sub UpdateProgress(ByVal i As Integer)
            Dim percent As Double = (CType(i, Double) / inputs) * 100
            Console.WriteLine("Percent complete: {0}", percent)
        End Sub
    End Class

End Module
namespace BCNonBlockingWithCancellation
{
    using System;
    using System.Collections.Concurrent;

    class ProgramWithCancellation
    {

        static int inputs = 2000;
        static void Main(string[] args)
        {
            // The token source for issuing the cancelation request.
            CancellationTokenSource cts = new CancellationTokenSource();

            // A blocking collection that can hold no more than 100 items at a time.
            BlockingCollection<int> numberCollection = new BlockingCollection<int>(100);

            // Set console buffer to hold our prodigious output.
            Console.SetBufferSize(80, 2000);

            // The simplest UI thread ever invented.
            Task.Factory.StartNew(() =>
                {
                    if (Console.ReadKey().KeyChar == 'c')
                        cts.Cancel();
                });

            // Start one producer and one consumer.
            Task.Factory.StartNew(() => NonBlockingConsumer(numberCollection, cts.Token));
            Task.Factory.StartNew(() => NonBlockingProducer(numberCollection, cts.Token));


            Console.WriteLine("Press the Enter key to exit.");
            Console.ReadLine();
        }

        static void NonBlockingConsumer(BlockingCollection<int> bc, CancellationToken ct)
        {
            // IsCompleted == (IsAddingCompleted && Count == 0)
            while (!bc.IsCompleted)
            {
                int nextItem = 0;
                try
                {
                    if (!bc.TryTake(out nextItem, 0, ct))
                    {
                        Console.WriteLine(" Take Blocked");
                    }
                    else
                        Console.WriteLine(" Take:{0}", nextItem);
                }

                catch (OperationCanceledException)
                {
                    Console.WriteLine("Taking canceled.");
                    break;
                }

                // Slow down consumer just a little to cause
                // collection to fill up faster, and lead to "AddBlocked"
                Thread.SpinWait(500000);
            }

            Console.WriteLine("\r\nNo more items to take. Press the Enter key to exit.");
        }

        static void NonBlockingProducer(BlockingCollection<int> bc, CancellationToken ct)
        {
            int itemToAdd = 0;
            bool success = false;

            do
            {
                // Cancellation causes OCE. We know how to handle it.
                try
                {
                    // A shorter timeout causes more failures.
                    success = bc.TryAdd(itemToAdd, 2, ct);
                }
                catch (OperationCanceledException)
                {
                    Console.WriteLine("Add loop canceled.");
                    // Let other threads know we're done in case
                    // they aren't monitoring the cancellation token.
                    bc.CompleteAdding();
                    break;
                }

                if (success)
                {
                    Console.WriteLine(" Add:{0}", itemToAdd);
                    itemToAdd++;
                }
                else
                {
                    Console.Write(" AddBlocked:{0} Count = {1}", itemToAdd.ToString(), bc.Count);
                    // Don't increment nextItem. Try again on next iteration.

                    //Do something else useful instead.
                    UpdateProgress(itemToAdd);
                }

            } while (itemToAdd < inputs);

            // No lock required here because only one producer.
            bc.CompleteAdding();
        }

        static void UpdateProgress(int i)
        {
            double percent = ((double)i / inputs) * 100;
            Console.WriteLine("Percent complete: {0}", percent);
        }
    }
}

請參閱

參考

System.Collections.Concurrent

概念

BlockingCollection 概觀