Översikt över BlockingCollection
BlockingCollection<T> är en trådsäker samlingsklass som innehåller följande funktioner:
En implementering av producent-konsument-mönstret.
Samtidig tillägg och tagning av objekt från flera trådar.
Valfri maximal kapacitet.
Infognings- och borttagningsåtgärder som blockeras när samlingen är tom eller full.
Infoga och ta bort "försök"-åtgärder som inte blockerar eller som blockerar upp till en angiven tidsperiod.
Kapslar in alla samlingstyper som implementerar IProducerConsumerCollection<T>
Annullering med annulleringstoken.
Två typer av uppräkning med
foreach
(For Each
i Visual Basic):Skrivskyddad uppräkning.
Uppräkning som tar bort objekt när de räknas upp.
Stöd för avgränsning och blockering
BlockingCollection<T> stöder avgränsning och blockering. Avgränsning innebär att du kan ange den maximala kapaciteten för samlingen. Avgränsning är viktigt i vissa scenarier eftersom det gör att du kan styra den maximala storleken på samlingen i minnet, och det förhindrar att de producerande trådarna rör sig för långt före de förbrukande trådarna.
Flera trådar eller uppgifter kan lägga till objekt i samlingen samtidigt, och om samlingen når sin angivna maximala kapacitet blockeras de producerande trådarna tills ett objekt tas bort. Flera användare kan ta bort objekt samtidigt, och om samlingen blir tom blockeras de förbrukande trådarna tills en producent lägger till ett objekt. En producerande tråd kan anropa CompleteAdding för att indikera att inga fler objekt kommer att läggas till. Konsumenter övervakar IsCompleted egenskapen för att veta när samlingen är tom och inga fler objekt läggs till. I följande exempel visas en enkel BlockingCollection med en begränsad kapacitet på 100. En producentaktivitet lägger till objekt i samlingen så länge ett externt villkor är sant och sedan anropar CompleteAdding. Konsumentaktiviteten tar objekt tills egenskapen IsCompleted är sann.
// 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)
Ett fullständigt exempel finns i How to: Add and Take Items Individually from a BlockingCollection (Så här lägger du till och tar objekt individuellt från en BlockingCollection).
Tidsblockeringsåtgärder
I tidsbunden blockering TryAdd och TryTake åtgärder för begränsade samlingar försöker metoden lägga till eller ta ett objekt. Om ett objekt är tillgängligt placeras det i variabeln som skickades in med referens och metoden returnerar true. Om inget objekt hämtas efter en angiven tidsgräns returnerar metoden false. Tråden är sedan fri att göra något annat användbart arbete innan du försöker igen för att komma åt samlingen. Ett exempel på tidsbaserad blockerande åtkomst finns i det andra exemplet i How to: Add and Take Items Individually from a BlockingCollection (Så här lägger du till och tar objekt individuellt från en BlockingCollection).
Avbryta åtgärder för att lägga till och ta
Åtgärderna Lägg till och Ta utförs vanligtvis i en loop. Du kan avbryta en loop genom att skicka en CancellationToken till metoden eller TryTake och sedan kontrollera värdet för tokens IsCancellationRequested egenskap för varje TryAdd iteration. Om värdet är sant är det upp till dig att svara på begäran om annullering genom att rensa alla resurser och avsluta loopen. I följande exempel visas en överlagring av TryAdd som tar en annulleringstoken och koden som använder den:
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
Ett exempel på hur du lägger till support för annullering finns i det andra exemplet i How to: Add and Take Items Individually from a BlockingCollection (Så här lägger du till och tar objekt individuellt från en BlockingCollection).
Ange samlingstyp
När du skapar en BlockingCollection<T>kan du ange inte bara den avgränsade kapaciteten utan även vilken typ av samling som ska användas. Du kan till exempel ange ett ConcurrentQueue<T> för fifo-beteende (first in-first out) eller ett ConcurrentStack<T> för lifo-beteende (last in-first out). Du kan använda valfri samlingsklass som implementerar IProducerConsumerCollection<T> gränssnittet. Standardsamlingstypen för BlockingCollection<T> är ConcurrentQueue<T>. I följande kodexempel visas hur du skapar en BlockingCollection<T> sträng som har en kapacitet på 1 000 och använder en ConcurrentBag<T>:
Dim bc = New BlockingCollection(Of String)(New ConcurrentBag(Of String()), 1000)
BlockingCollection<string> bc = new BlockingCollection<string>(new ConcurrentBag<string>(), 1000 );
Mer information finns i Så här lägger du till avgränsnings- och blockeringsfunktioner i en samling.
IEnumerable Support
BlockingCollection<T> innehåller en GetConsumingEnumerable metod som gör det möjligt för konsumenter att använda foreach
(For Each
i Visual Basic) för att ta bort objekt tills samlingen har slutförts, vilket innebär att den är tom och att inga fler objekt läggs till. Mer information finns i How to: Use ForEach to Remove Items in a BlockingCollection (Använda ForEach för att ta bort objekt i en BlockingCollection).
Använda många BlockingCollections som ett
För scenarier där en konsument behöver ta objekt från flera samlingar samtidigt kan du skapa matriser med BlockingCollection<T> och använda statiska metoder som TakeFromAny och AddToAny som kommer att lägga till i eller ta från någon av samlingarna i matrisen. Om en samling blockerar försöker metoden omedelbart en annan tills den hittar en som kan utföra åtgärden. Mer information finns i How to: Use Arrays of Blocking Collections in a Pipeline (Använda matriser med blockeringssamlingar i en pipeline).