For each call emitted to server, I create a new timer by Task.Delay to watch on its timeout.
Let's say there would be hundreds of concurrent calls. Hence there would be hundreds of Task counting the timer.
I guess the internal implementation of TPL considered this occasion and all the tasks rely on the same underlying timer?
I am not quite understand the mechanism how the Task.Delay works internally.
The Delay method is typically used to delay the operation of all or part of a task for a specified time interval. Most commonly, the time delay is introduced: At the beginning of the task, as the following example shows.
Task. Delay does not create new Thread, but still may be heavy, and no guaranties on order of execution or being precise about deadlines.
Delay will create a task which will complete after a time delay. Task. Delay is not blocking the calling thread so the UI will remain responsive.
This function would return immediately. The await keyword is what is causing your execution to be delayed. and now it will delay for 1 second because it is waiting to get the result of the created task for 1 second.
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 CancellationTokens 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
CancellationTokenfor each of thousands upon thousands of asynchronous calls being made per second. That’s a lot ofTimerandCancellationTokenSourceinstances."
If approximated delay is acceptable, an alternative is to replace Task.Delay with HashedWheelTimer.
Code example.
HashedWheelTimer timer = new HashedWheelTimer();
await timer.Delay(1000);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With