当有数百个Task.Delay时,性能如何?

Mr.*_*oor 9 .net c# timer task-parallel-library async-await

对于发送到服务器的每个调用,我创建一个新的计时器Task.Delay来监视它的超时.

假设会有数百个并发呼叫.因此会有数百个Task计数器计时器.

我想TPL的内部实现考虑了这个场合,所有的任务都依赖于相同的底层计时器?

我不太了解Task.Delay内部工作的机制.

i3a*_*non 10

Task.Delay是用内部实现的System.Threading.Timer.该计时器类是单个本机计时器之上的包装器.要同步对该单个本机计时器的访问,可以AppDomain创建新计时器(并更改现有计时器)的级别锁定.您可以在参考源中看到:

internal bool Change(uint dueTime, uint period)
{
    // ...
    lock (TimerQueue.Instance)
    {
        // ...
    }
    // ...
}
Run Code Online (Sandbox Code Playgroud)

在大多数情况下,这很好,但是当你每秒创建相当数量的这些计时器时,你可以在该锁上获得重要的争用.实际知道的唯一方法是在真实环境中分析您的应用程序.


我个人已经通过CancellationTokenSource使用计时器创建了太多自我取消来达到这一点(你可以看到我在我的博客上避免这种情况:令人惊讶的争用System.Threading.Timer).

还有Stephen Toub的这篇文章关于来自Timeouts的Coalescing CancellationTokens提到:

"当然,总会出现推动性能界限的情况,我们最近看到了一些高吞吐量的情况,人们CancellationToken每秒都会为成千上万的异步调用中的每一个创建一个这样的情况.这就是很多TimerCancellationTokenSource实例."

  • 我总是在 BCL 中发现全局锁令人惊讶。另一个在 ExceptionDispatchInfo 中。它限制了 await 中异常处理的扩展。 (3认同)

Mr.*_*oor 5

如果近似延迟是可以接受的,另一种方法是Task.DelayHashedWheelTimer替换。

代码示例。

HashedWheelTimer timer = new HashedWheelTimer();
await timer.Delay(1000);
Run Code Online (Sandbox Code Playgroud)