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

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

对于发送到服务器的每个调用,我通过 Task.Delay 创建一个新计时器以观察其超时。

假设会有数百个并发调用。因此会有数百个 Task 计算计时器。

我猜 TPL 的内部实现考虑了这种情况并且所有任务都依赖于相同的底层计时器?

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

Task.Delay 是用内部 System.Threading.Timer 实现的。该计时器 class 是单个本机计时器之上的包装器。要同步对单个本机计时器的访问,在创建新计时器(和更改现有计时器)时需要 AppDomain 级别锁定。您可以在 reference source:

中看到
internal bool Change(uint dueTime, uint period)
{
    // ...
    lock (TimerQueue.Instance)
    {
        // ...
    }
    // ...
}

在大多数情况下这很好,但是当您每秒创建大量此类计时器时,您可能会对该锁产生重大争用。真正知道的唯一方法是在真实环境中分析您的应用程序


就个人而言,我已经达到了这一点,因为我使用计时器创建了太多的自取消 CancellationTokenSource(您可以在我的博客上看到我是如何避免这种情况的:Surprising Contention In System.Threading.Timer)。

Stephen Toub 关于 Coalescing CancellationTokens from Timeouts 的 post 也提到了:

"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."

如果近似延迟是可以接受的,另一种方法是将 Task.Delay 替换为 HashedWheelTimer

代码示例。

HashedWheelTimer timer = new HashedWheelTimer();
await timer.Delay(1000);