对于发送到服务器的每个调用,我按Task.Delay
创建一个新的计时器来监视其超时。
让我们说会有数百个并发呼叫。因此,会有数百Task
个计时器。
我想TPL的内部实现考虑了这个场合,所有的任务都依赖于相同的底层计时器?
我不太了解Task.Delay
内部如何运作的机制。
答案 0 :(得分:9)
Task.Delay
使用内部System.Threading.Timer
实施。该计时器类是单个本机计时器之上的包装器。要同步对该单个本地计时器的访问,在创建新计时器(以及更改现有计时器)时会有AppDomain
级别锁定。您可以在reference source:
internal bool Change(uint dueTime, uint period)
{
// ...
lock (TimerQueue.Instance)
{
// ...
}
// ...
}
在大多数情况下,这很好,但是当你每秒创建相当数量的这些计时器时,你可能会对该锁定产生重大争议。实际知道的唯一方法是在真实环境中分析您的应用程序。
我个人已经通过使用计时器创建了太多自我取消CancellationTokenSource
来达到这一点(你可以在我的博客上看到我如何避免这种情况:Surprising Contention In System.Threading.Timer
)。
Stephen Toub的这篇文章还提到Coalescing CancellationToken
s from Timeouts提及:
"当然,总会出现推动性能界限的情况,我们最近看到了一些高吞吐量的案例,人们为每千人创建了一个这样的
CancellationToken
每秒进行数千次异步调用。这是很多Timer
和CancellationTokenSource
个实例。"
答案 1 :(得分:1)
如果可以接受近似延迟,则可以选择将Task.Delay
替换为HashedWheelTimer。
代码示例。
HashedWheelTimer timer = new HashedWheelTimer();
await timer.Delay(1000);