How is the performance when there are hundreds of Task.Delay
Task.Delay
is implemented with an internal System.Threading.Timer
. That timer class is a wrapper on top of a single native timer. To synchronize access to that single native timer there's an AppDomain
level lock on creating new timers (and changing existing ones). You can see that in the reference source:
internal bool Change(uint dueTime, uint period)
{
// ...
lock (TimerQueue.Instance)
{
// ...
}
// ...
}
In most cases that's fine, but when you create a considerable amount of these timers per second you can get significant contention on that lock. The only way to actually know is to profile your application in a real environment.
I, personally, have reached that point by creating too many self-cancelling CancellationTokenSource
using timers (you can see how I avoided that on my blog: Surprising Contention In System.Threading.Timer
).
There's also this post by Stephen Toub about Coalescing CancellationToken
s from Timeouts that mentions:
"Of course, there are always scenarios the push the boundaries of performance, and we’ve recently seen some high-throughput cases where folks were creating one such
CancellationToken
for each of thousands upon thousands of asynchronous calls being made per second. That’s a lot ofTimer
andCancellationTokenSource
instances."
If approximated delay is acceptable, an alternative is to replace Task.Delay
with HashedWheelTimer.
Code example.
HashedWheelTimer timer = new HashedWheelTimer();
await timer.Delay(1000);