Search code examples
c#performanceblockingcollection

Should BlockingCollection.TryTake(object,TimeSpan) return immediately on new data?


I am trying to ascertain why performance on my blocking collection appears slow. A simple version of my code is illustrated in the question further below.

My question here is if BlockingCollection.TryTake(object,TimeSpan) returns immediately on new data?

TimeSpan gridNextTS = new TimeSpan(0, 0, 60);

if (trPipe.TryTake(out tr, gridNextTS) == false)

From my testing it appears that data is NOT returned immediately. Does that seems likely desired behaviour or am I using it incorrectly?

Detail of code previous question:

Consumer/Producer with BlockingCollection appears slow


Solution

  • A concise benchmark shows that BlockingCollection<T> does, in fact, perform the handover pretty swiftly regardless of the timeout value supplied to TryTake.

    public async Task BlockingCollectionPerformance()
    {
        using (var collection = new BlockingCollection<int>())
        {
            var consumer = Task.Run(() =>
            {
                var i = 0;
    
                while (collection.TryTake(out i, TimeSpan.FromSeconds(2)))
                {
                    Debug.Print(i.ToString());
                }
            });
    
            var producer = Task.Run(() =>
            {
                try
                {
                    for (var i = 0; i < 10; i++)
                    {
                        collection.Add(i);
                    }
                }
                finally
                {
                    collection.CompleteAdding();
                }
            });
    
            await Task.WhenAll(producer, consumer);
        }
    }
    

    The above completes in ~3 ms on my box.

    To be more specific though, TryTake returns quickly whenever an item is added to the collection (and TryTake returns true), or when you call CompleteAdding on the blocking collection (in which case there is no point in waiting out the timeout, and TryTake returns false). It is possible to shoot yourself in the foot by keeping the consumer blocked longer than necessary if you never call CompleteAdding in which case TryTake will have to wait out the full timeout length before returning false.