Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How is the performance when there are hundreds of Task.Delay

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.

like image 228
Mr.Wang from Next Door Avatar asked Aug 21 '15 04:08

Mr.Wang from Next Door


People also ask

What is Task delay?

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.

Does task delay create a new thread?

Task. Delay does not create new Thread, but still may be heavy, and no guaranties on order of execution or being precise about deadlines.

Does task delay block?

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.

What is await task delay?

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.


2 Answers

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 CancellationToken for each of thousands upon thousands of asynchronous calls being made per second. That’s a lot of Timer and CancellationTokenSource instances."

like image 116
i3arnon Avatar answered Oct 24 '22 06:10

i3arnon


If approximated delay is acceptable, an alternative is to replace Task.Delay with HashedWheelTimer.

Code example.

HashedWheelTimer timer = new HashedWheelTimer();
await timer.Delay(1000);
like image 5
Mr.Wang from Next Door Avatar answered Oct 24 '22 07:10

Mr.Wang from Next Door