How to empty a BlockingCollection
Asked Answered
U

6

25

I have a thread adding items to a BlockingCollection .

On another thread I am using foreach (var item in myCollection.GetConsumingEnumerable())

If there is a problem I want to break out of my foreach and my method and clear whatever is left in the BlockingCollection however I can't find a way to do it.

Any ideas?

Urita answered 3/11, 2011 at 20:4 Comment(0)
L
9

Possibly use the overload of GetConsumingEnumerable that takes a CancellationToken; then, if anything goes wrong from the producing side, it can cancel the consumer.

Lucianolucias answered 3/11, 2011 at 20:8 Comment(5)
Can I call the CancellationToken.Cancel within the foreach of GetConsumingEnumerbale?Urita
I can do it within the GetConsumingEnumerable but it throws a OperationCanceledException. A simple break will leave the foreach loop but the BlockingCollection will still have items in itUrita
@Jon: I wasn't suggesting cancelling in the consumer, but in the producer - I thought that was where you were detecting the error. If the consumer has noticed the problem, can't it just consume everything until there's nothing left? Does it need to notify the producer to say that it doesn't want any more items? Do you even need the same blocking collection any more? Couldn't you just throw it away?Lucianolucias
I decided upon using a break in the consumer and then when I start producing again I just create a new instance of the BlockingCollectionUrita
A note to the unwary, GetConsumingEnumerable will block once the collection is empty (waiting on more entries) unless you've previously called CompleteAdding. It's in the sample code block, but not mentioned in the method description.Elianore
C
24

Just take out all remaining items:

while (collection.TryTake(out _)){}
Carmine answered 3/4, 2018 at 15:1 Comment(2)
Didn't know that _ kan be used in an out statement. Double benefit! (Would like to upvote this answer twise :-)Hefty
Best one! works like a charm!Weathertight
A
19

I'm using this extension method:

public static void Clear<T>(this BlockingCollection<T> blockingCollection)
{
    if (blockingCollection == null)
    {
        throw new ArgumentNullException("blockingCollection");
    }

    while (blockingCollection.Count > 0)
    {
        T item;
        blockingCollection.TryTake(out item);
    }
}

I'm wondering if there's a better, less hacky, solution.

Almaalmaata answered 10/7, 2012 at 10:30 Comment(7)
With the new out var syntax: while (blockingCollection.TryTake(out var _)){}Lamkin
any particular reason why TryTake instead of just plain old Take? Is there any scenario where the try version would return false in this example?Tallinn
@Tallinn There's a race condition between the while check and TryTake. Another thread, for example, could take the last element out and Take would raise an InvalidOperationException.Almaalmaata
@PaoloMoretti Ah, I see. In my use case I only had one consumer so I didn't think of that :)Tallinn
Similar to Kjellski's comment, but without requiring the new syntax, this could be shortened to T _; while (blockingCollection.TryTake(out _)){}Gutturalize
Similar to @Timbo's comment (if we're going for code golf), it can be shorten to just while (blockingCollection.TryTake(out _));Disciplinant
@Disciplinant feel write to update the answer, if you like :-) That syntax was not supported in 2012 by the C# 4.0 compilerAlmaalmaata
L
9

Possibly use the overload of GetConsumingEnumerable that takes a CancellationToken; then, if anything goes wrong from the producing side, it can cancel the consumer.

Lucianolucias answered 3/11, 2011 at 20:8 Comment(5)
Can I call the CancellationToken.Cancel within the foreach of GetConsumingEnumerbale?Urita
I can do it within the GetConsumingEnumerable but it throws a OperationCanceledException. A simple break will leave the foreach loop but the BlockingCollection will still have items in itUrita
@Jon: I wasn't suggesting cancelling in the consumer, but in the producer - I thought that was where you were detecting the error. If the consumer has noticed the problem, can't it just consume everything until there's nothing left? Does it need to notify the producer to say that it doesn't want any more items? Do you even need the same blocking collection any more? Couldn't you just throw it away?Lucianolucias
I decided upon using a break in the consumer and then when I start producing again I just create a new instance of the BlockingCollectionUrita
A note to the unwary, GetConsumingEnumerable will block once the collection is empty (waiting on more entries) unless you've previously called CompleteAdding. It's in the sample code block, but not mentioned in the method description.Elianore
G
7

This worked for me

while (bCollection.Count > 0)
{
    var obj = bCollection.Take();
    obj.Dispose();
}

Take() removes from the collection and you can call any clean up on your object and the loop condition does not invoke any blocking calls.

Gosselin answered 10/9, 2014 at 14:43 Comment(2)
This answer turned up in the low quality review queue, presumably because you didn't explain the code. If you do explain it (in your answer), you are far more likely to get more upvotes—and the questioner actually learns something!Paigepaik
This is inefficient, because depending on the underlying collection you are iterating over each element to determine the count, just to remove the first element. Also, the Dispose is unrelated to the question.Superpatriot
C
1
BlockingCollection<T> yourBlockingCollection = new BlockingCollection<T>();

I assumed you mean clear your blocking collection. Jon's answer is more appropriate to your actual question I think.

Canticle answered 3/11, 2011 at 20:9 Comment(2)
I meant I wanted to exit the foreach loop and empty whatever is in the BlockingCollectionUrita
can you please explain to me what the difference between removing those last items, and just: theBlockingCollectionUsedIntheForEachLoop = new BlockingCollection<T>(); is???Armourer
D
-7

For just clearing the collection you can do:

myBlockingCollection.TakeWhile<*MyObject*>(qItem => qItem != null);

or just

myBlockingCollection.TakeWhile<*MyObject*>(qItem => true);
Desmid answered 10/1, 2013 at 15:43 Comment(1)
The TakeWhile is a LINQ porting and does not remove from the collection. It just creates a copyTomkin

© 2022 - 2024 — McMap. All rights reserved.