Async Targeting Pack的发布促使我使用ILSpy来查看那里提供了哪些基于任务的异步模式 (TAP)扩展方法(其中一些我已经自己实现以在 VS2010 中使用)。我偶然发现了 for 的.CancelAfter(TimeSpan)
方法CancellationTokenSource
(它是 .NET 4.0 的 Async Targeting Pack 中的扩展方法,但在 .NET 4.5 中是实例方法),并认为这可能是一种为各种操作实现超时的好方法t 本机有超时,但支持取消。
但是查看 Async Targeting Pack 中的实现,似乎如果关联Task
完成或取消,则计时器继续运行。
/// <summary>Cancels the <see cref="T:System.Threading.CancellationTokenSource" /> after the specified duration.</summary>
/// <param name="source">The CancellationTokenSource.</param>
/// <param name="dueTime">The due time in milliseconds for the source to be canceled.</param>
public static void CancelAfter(this CancellationTokenSource source, int dueTime)
{
if (source == null)
{
throw new NullReferenceException();
}
if (dueTime < -1)
{
throw new ArgumentOutOfRangeException("dueTime");
}
Timer timer = new Timer(delegate(object self)
{
((IDisposable)self).Dispose();
try
{
source.Cancel();
}
catch (ObjectDisposedException)
{
}
});
timer.Change(dueTime, -1);
}
假设我使用此方法为经常使用的基于 TAP 的操作提供超时,并用.CancelAfter()
. 现在假设用户提供了 5 分钟(300 秒)的超时值,并每秒调用此操作 100 次,所有操作均在几毫秒后成功完成。在每秒 100 次调用的 300 秒之后,所有这些操作将累积 30,000 个运行计时器,即使这些任务很久以前就成功完成了。他们最终都会过去并运行上面的委托,这可能会抛出一个ObjectDisposedException
等。
这不是有点泄漏,不可扩展的行为吗?当我实现超时时,我使用了Task/TaskEx.Delay(TimeSpan, CancellationToken)
,并且当相关任务结束时,我取消了,.Delay()
以便停止并释放计时器(毕竟它是一个 IDisposable,它确实包含非托管资源)。这种清理是不是过于热心了?让数以万计的定时器同时运行(并可能在以后抛出数以万计的捕获异常)的成本对于普通应用程序的性能真的无关紧要吗?.CancelAfter()
与正在完成的实际工作相比,开销和泄漏几乎总是微不足道的,通常应该被忽略吗?