Werken met BlockingCollection in C #

Overweeg een scenario waarin meerdere threads een wachtrij lezen en ernaar schrijven. Meer specifiek kan het zijn dat er tegelijkertijd meerdere producenten zijn die gegevens opslaan en meerdere consumenten die ze ophalen uit een gemeenschappelijke gegevensopslag. Daarom heeft u een goed synchronisatiemechanisme nodig om de toegang tot deze gegevens te synchroniseren.

Dit is precies waar de klasse BlockingCollection te hulp komt. Hoewel er veel andere manieren zijn, biedt deze klasse een van de meest efficiënte manieren om de toegang tot uw gegevens te synchroniseren. De klasse BlockingCollection behoort tot de naamruimte System.Collections.Concurrent.

Wat is een BlockingCollection?

De BlockingCollection is een threadveilige verzameling waarin u meerdere threads tegelijkertijd gegevens kunt toevoegen en verwijderen. Het wordt weergegeven in .Net via de klasse BlockingCollection; u kunt deze klasse gebruiken om een ​​producent-consument patroon te implementeren.

In het producent-consumentpatroon heb je twee verschillende componenten die op twee verschillende threads draaien. Deze omvatten een producentcomponent die bepaalde gegevens produceert die naar de wachtrij worden gepusht, en een consument die de gegevens gebruikt die in de wachtrij zijn opgeslagen. Als u een BlockingCollection gebruikt, kunt u de begrensde capaciteit specificeren, evenals het type collectie dat u wilt gebruiken.

Het type BlockingCollection fungeert als een wrapper over een instantie van het type IProducerConsumerCollection. Met andere woorden, het fungeert als een wrapper over een andere collectie die op zijn beurt de IProducerConsumerCollection-interface implementeert. De klassen ConcurrentBag, ConcurrentQueue en ConcurrentStack kunnen bijvoorbeeld worden gebruikt met een BlockingCollection, aangezien ze allemaal de interface IProducerConsumerCollection implementeren.

Merk op dat de IProducerConsumerCollection-interface declaratie bevat van methoden die kunnen worden gebruikt om te werken met thread-safe collecties. De MSDN stelt: "Definieert methoden om threadveilige collecties te manipuleren die bedoeld zijn voor gebruik door producenten / consumenten. Deze interface biedt een uniforme weergave voor verzamelingen van producenten / consumenten, zodat abstracties op een hoger niveau, zoals System.Collections.Concurrent.BlockingCollection, de collectie kunnen gebruiken als het onderliggende opslagmechanisme. "

Het volgende codefragment laat zien hoe u een instantie van een BlockingCollection van strings kunt maken.

var blockingCollection = new BlockingCollection();

Wanneer u een BlockingCollection gebruikt, kunt u gegevens aan de verzameling toevoegen met behulp van de Add-methode of de TryAdd-methode. Laten we nu het verschil tussen deze twee methoden begrijpen.

BlockingCollection data = new BlockingCollection(boundedCapacity: 3);

data.Add(1);

data.Add(2);

data.Add(3);

data.Add(4); //This would block until an item is removed from the collection.

Merk op hoe we boundedCapacity hebben gespecificeerd bij het maken van een instantie van een BlockingCollection zoals getoond in het bovenstaande codefragment. Dit wordt gespecificeerd om de begrensde grootte van de verzamelinginstantie aan te geven.

U kunt ook de methode TryAdd gebruiken om een ​​item toe te voegen aan een BlockingCollection-instantie. Bij deze methode kunt u een time-outwaarde gebruiken. Als de add-bewerking binnen de opgegeven tijd mislukt, retourneert de methode TryAdd false. Het volgende codefragment laat zien hoe u kunt profiteren van de TryAdd-methode om een ​​item toe te voegen aan een exemplaar van BlockingCollection.

BlockingCollection data = new BlockingCollection(boundedCapacity: 3);

data.Add(1);

data.Add(2);

data.Add(3);

if (data.TryAdd(4, TimeSpan.FromMilliseconds(100)))

{

   Console.WriteLine("A new item was successfully added to the collection.");

}

else

{

   Console.WriteLine("Failed to add a new item to the collection.");

}

Om een ​​item uit een BlockingCollection te verwijderen, kun je de Take of de TryTake methode gebruiken. Merk op dat de Take-methode blokkeert als er geen items in de collectie zijn en deblokkeert zodra een nieuw item aan de collectie wordt toegevoegd. De methode TryTake kan ook worden gebruikt om een ​​item te verwijderen uit een instantie van een BlockingCollection. U kunt met deze methode een time-outwaarde opgeven, zodat de methode blokkeert (totdat de opgegeven tijd is verstreken) totdat een item aan de verzameling wordt toegevoegd. Als een item gedurende deze tijd niet uit de verzameling kon worden verwijderd (de opgegeven time-out), retourneert de methode TryTake false.

Het volgende codefragment illustreert hoe de TryTake-methode kan worden gebruikt om een ​​item te verwijderen uit een instantie van het type BlockingCollection.

int item;

while (data.TryTake(out item, TimeSpan.FromMilliseconds(100)))

{

   Console.WriteLine(item);

}

Hier is een volledige codelijst ter referentie. Dit programma illustreert hoe u een BlockingCollection kunt gebruiken om items aan en uit een collectie toe te voegen en te verwijderen.

 class Program

   {

       private static BlockingCollection data = new BlockingCollection();

       private static void Producer()

       {

           for (int ctr = 0; ctr < 10; ctr++)

           {

               data.Add(ctr);

               Thread.Sleep(100);

           }

       }

       private static void Consumer()

       {

           foreach (var item in data.GetConsumingEnumerable())

           {

               Console.WriteLine(item);

           }

       }

       static void Main(string[] args)

       {

           var producer = Task.Factory.StartNew(() => Producer());

           var consumer = Task.Factory.StartNew(() => Consumer());

           Console.Read();

       }

   }