Search code examples
c#multithreadingblockingcollection

BlockingCollection.Add in a loop with await taking considerable more time than expected


Here is the simplest repro I could make illustrating the issue. This is a Console application that creates a bounded BlockingCollection<int>, starts a background thread that consumes items from the collection using TryTake and then Adds items on the main thread in a loop up to the upper bound limit like this:

// Measure the time it takes to add items to the collection with one millisecond delay between Adds.
var sw = Stopwatch.StartNew();
for (int i = 0; i < collection.BoundedCapacity; i++)
{
  collection.Add(i);
  await Task.Delay(millisecondsDelay: 1); 
}
sw.Stop();

The BoundedCapacity is 1000 items, so the for loop should not take much more time than one second. However, as the comments in the sample code explain, the elapsed time is always greater than the max expected time (which is set to 2 * BoundedCapacity milliseconds) - on my machine under debugger, the times are in the range of 3.2 - 4 seconds, while when running without debugger, the times are in the range of 15 - 16 seconds!

When you comment out the line await Task.Delay(millisecondsDelay: 1), the program behaves as expected (the loop finishes in a few milliseconds).

I have no idea what is going on here. Can somebody please explain the behavior?

UPDATE: This was caused my ignorance to not knowing that the Windows timer resolution is roughly 15 milliseconds - see answer by Simon Kocourek below.


Solution

  • From the docs you can read that the lowest resolution window on Windows is 15ms:

    This method depends on the system clock. This means that the time delay will approximately equal the resolution of the system clock if the millisecondsDelay argument is less than the resolution of the system clock, which is approximately 15 milliseconds on Windows systems.

    Meaning that 1000 calls to Delay(1) should take roughly 15s instead of 1s.