Async Targeting Pack的发布促使我使用ILSpy来了解那里提供了哪些Task-based Asynchronous Pattern (TAP)扩展方法(其中一些我已经实现了自己的方法,可在VS2010中使用)。我偶然发现了.CancelAfter(TimeSpan)
的 CancellationTokenSource
方法(这是.NET 4.0的异步定位包中的扩展方法,但在.NET 4.5中是实例方法),并认为这可能是对各种操作实现超时的好方法 native 没有超时,但支持取消。
但是查看异步目标包中的实现,似乎如果关联的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个运行计时器,即使这些任务很早以前就已经成功完成了。他们最终都会过去并运行上面的委托(delegate),这可能会抛出ObjectDisposedException
等。这难道不是一种泄漏性的,不可扩展的行为吗?当我实现超时时,我使用了
Task/TaskEx.Delay(TimeSpan, CancellationToken)
,并且当关联的任务结束时,我取消了.Delay()
,以便计时器将停止并被处置(毕竟这是一个IDisposable,并且确实包含非托管资源)。这清理工作过分热心吗?同时运行成千上万个计时器(可能在以后抛出成千上万个捕获的异常)的成本真的与普通应用程序的性能无关吗?与实际完成的工作相比,.CancelAfter()
的开销和泄漏性几乎总是微不足道的,并且通常应该忽略不计吗? 最佳答案
只需尝试一下,将其推到极限,看看会发生什么。我无法通过一千万个计时器将工作量超过90 MB。 System.Threading.Timer非常便宜。
using System;
using System.Threading;
class Program {
public static int CancelCount;
static void Main(string[] args) {
int count = 1000 * 1000 * 10;
for (int ix = 0; ix < count; ++ix) {
var token = new CancellationTokenSource();
token.CancelAfter(500);
}
while (CancelCount < count) {
Thread.Sleep(100);
Console.WriteLine(CancelCount);
}
Console.WriteLine("done");
Console.ReadLine();
}
}
static class Extensions {
public static void CancelAfter(this CancellationTokenSource source, int dueTime) {
Timer timer = new Timer(delegate(object self) {
Interlocked.Increment(ref Program.CancelCount);
((IDisposable)self).Dispose();
try {
source.Cancel();
}
catch (ObjectDisposedException) {
}
});
timer.Change(dueTime, -1);
}
}
关于c# - CancellationTokenSource.CancelAfter()是否泄漏?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/10715688/