مشاركة عبر


كيفية القيام بما يلي: إضافة و أخذ العناصر بشكل فردي من BlockingCollection

يوضح هذا المثال كيفية إضافة وإزالة العناصر من BlockingCollection<T> في كلا الطريقتين الحظر و غير الحظر. لمزيد من المعلومات حول BlockingCollection<T> راجع نظرة عامة BlockingCollection.

للحصول على مثال عن كيفية تعداد BlockingCollection<T> حتى تصبح فارغة ولن يتم اضافة عناصر أخرى ، راجعكيفية القيام بما يلي: استخدم 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()
                                      While numbers.IsCompleted = False
                                          Dim i As Integer = numbers.Take()
                                          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(() =>
            {
                while (!numbers.IsCompleted)
                {
                    int i = numbers.Take();
                    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() يإرجاع غير صحيح. وهذا يسمح لمؤشر الترابط بأن يقوم بأعمال أخرى مفيدة لبعض الوقت , ثم يحاول مرة أخرى لاحقاً أما استرداد عنصر جديد ,أو إضافة نفس العنصر الذي لم يستطع إضافته مسبقاً. كما يوضح البرنامج أيضا كيفية تطبيق الإلغاء عند الوصول إلى 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