0

I recently needed an IProducerConsumerCollection<T> implementation but I wanted it to block on TryAdd if a certain capacity has been reached and block on TryTake if it's empty. I was certain that BlockingCollection is actually an implementation of IProducerConsumerCollection<T> but realised that this isn't the case. Why is that?

Which property of BlockingCollection doesn't make it fit to implement the IProducerConsumerCollection interface?

I understand that BlockingCollection is a wrapper around IProducerConsumerCollection but irrespective I thought that itself should be implementing the same interface.

Lucas Trzesniewski
  • 50,214
  • 11
  • 107
  • 158
Yannis
  • 6,047
  • 5
  • 43
  • 62
  • My *guess* is that it's to prevent "double wrapping" - given that the main purpose of `BlockingCollection` is to wrap `IProducerConsumerCollection`, why would you want to create a *second* `BlockingCollection` from the first one? – Jon Skeet Jan 11 '15 at 11:06
  • I get the double wrapping but that isn't a reason that's a problem of the current implementation. I mean what's the way currently to implement the IProducerConsumerCollection in a blocking way if not with double wrapping? – Yannis Jan 11 '15 at 11:13
  • 3
    No, the point is that double-wrapping is a bad idea - the current API prevents you from doing it. If you've *got* a `BlockingCollection` and you *want* a `BlockingCollection`, you should just use it rather than wrapping it. If you're after an `IProducerConsumerCollection` for a reason other than creating a `BlockingCollection`, what's that reason? (There may be one, but creating a `BlockingCollection` is the most common one.) – Jon Skeet Jan 11 '15 at 11:15
  • you can simply use semaphores for handling this. before `TryTake` and after successful `TryAdd`. – Hamid Pourjam Jan 11 '15 at 11:31
  • You might want to take a good look at your shutdown code, impossible to do cleanly if you only have an IProducerConsumerCollection. – Hans Passant Jan 11 '15 at 12:49

1 Answers1

0

Literally answering the question in the title for your question seems academic and not all that useful. Jon has offered some insight as to what a likely answer for that specific question might be. But let's try to address the underlying need you seem to have…

I recently needed an IProducerConsumerCollection implementation but I wanted it to block on TryAdd if a certain capacity has been reached and block on TryTake if it's empty.

These are features that BlockingCollection already provides. The fact that it doesn't implement IProducerConsumerCollection isn't an impediment at all, nor does the reason it doesn't implement that interface seem to be relevant to your actual stated need.

  1. "I wanted it to block on TryAdd if a certain capacity has been reached"

Having the TryAdd() method block doesn't make any sense. The whole reason for that method is to have a non-blocking add operation (or blocking with a timeout). I.e. to try to perform an add operation, but with the possibility that it might fail.

Instead, initialize the BlockingCollection with one of the constructors that allows you to pass an int capacity value, and then use the Add() method to actually add something to the collection. The Add() method will block until the collection's current size is less than this capacity.

Note that the TryAdd() method respects the collection capacity as well; but it treats a full collection as an immediate failure of the add operation. This seems exactly opposite of what you want; i.e. to block until the add operation is assured of success. For that, the Add() method is definitely the one to use.

  1. "and block on TryTake if it's empty"

For the same reason that having TryAdd() block doesn't make sense, nor does having TryTake() block make sense.

Instead, call GetConsumingEnumerable() and use the object returned to retrieve items from the collection. If the collection is emptied, the IEnumerable<T>.MoveNext() method will block until either a new item is added to the collection, or you call the CompleteAdding() method.


If the above does not address your need, then you should edit your question to explain precisely what it is you do want to accomplish. Even if someone gave you a complete, precise answer to exactly the question you ask in your title, that's not going to help you write any code that actually does something. IMHO, you should focus more on what it is you're trying to do, so that we can help you with that.

Peter Duniho
  • 68,759
  • 7
  • 102
  • 136
  • The question isn't meant to be here for literature purposes. You are right that TryAdd TryTake make no sense for blocking collections but only used (in my example above) those methods as those are the ones provided in the interface - i.e. to avoid confusion. Apologies if the effect was the opposite. The reason I posted the question is because in a framework / library code where you are thinking of extensibility you want people to pass different versions of IProducerConsumerCollection to achieve different needs. Because of that I looked into the .NET framework and this question came up. – Yannis Jan 11 '15 at 23:08