0

Is it possible to have a BlockingCollection<T> (JobQueue in my example) block execution on both the GetConsumingEnumerable() stream AND on some other criteria?

I have the condition availableSlots > 0 which only allows items to be consumed when there are available slots. The problem is that the foreach indefinitely loops when there are items in the collection but the condition is false.

Can I not get the collection to block on availableSlots > 0 as well?

foreach (var job in JobQueue.GetConsumingEnumerable())
{
    if(availableSlots > 0)
    {
        JobHandler jobHandler = job;

        Task.Factory.StartNew(() =>
        {
            ExecuteJob(jobHandler);
        });
    }
}

Perhaps I am using this collection incorrectly. Any help appreciated!

Dave New
  • 38,496
  • 59
  • 215
  • 394

2 Answers2

2

If you want to block while the value is 0, you will need additional synchronization for this. I think the right solution for you is SemaphoreSlim, because it does exactly what you need: waiting while its value is 0.

With that, the code would look something like:

SemaphoreSlim slotsSemaphore = new SemaphoreSlim(…);

…

foreach (var job in JobQueue.GetConsumingEnumerable())
{
    slotsSemaphore.Wait();

    JobHandler jobHandler = job;

    Task.Factory.StartNew(() =>
    {
        try
        {
            ExecuteJob(jobHandler);
        }
        finally
        {
            slotsSemaphore.Release();
        }
    });
}
svick
  • 236,525
  • 50
  • 385
  • 514
  • Am confused with this approach, How does this work? when we set `initialCount` to `0` and call `Wait()` it blocks indefinitely right? How'll we release it? – Sriram Sakthivel Oct 01 '13 at 09:06
  • When you create `SemaphoreSlim`, you usually set it to some non-zero value. `Wait()` then decreases the value (after wait, if it can't do it right away) and `Release()` increases the value. – svick Oct 01 '13 at 12:11
  • You don't need additional synchronization for blocking while the count is 0. Hence the name "BlockingCollection" - the collection blocks on Take(), TryTake(), and GetConsumingEnumerable() when empty. This class was written specifically so that you don't need to implement blocking/signalling yourself. – Dave Black May 15 '15 at 20:25
  • @DaveBlack Have you read the question? The "value" in my answer is not the count of items in the collection, it's not some other value, which is what this question is about. – svick May 15 '15 at 21:05
0

Not sure this is the best way, but putting my option forward.

Why not just wait until it is true?

while (availableSlots <= 0)
{
    Thread.Sleep(1);//arbitary sleep
}

JobHandler jobHandler = job;
...

or use SpinWait

SpinWait.SpinUntil(() => availableSlots > 0);

JobHandler jobHandler = job;
...

Third option is to use ManualResetEvent or AutoResetEvent

signal.Waitone();

JobHandler jobHandler = job;
...

And set the signal when you change the value of availableSlots.

Sriram Sakthivel
  • 72,067
  • 7
  • 111
  • 189