All*_*nek 5 .net c# .net-4.0 task-parallel-library .net-4.5
Async Targeting Pack的发布促使我使用ILSpy来查看那里提供了哪些基于任务的异步模式(TAP)扩展方法(其中一些我自己已经实现了在VS2010中使用).我偶然发现了这个.CancelAfter(TimeSpan)方法CancellationTokenSource(这是.NET 4.0异步目标包中的扩展方法,但它是.NET 4.5中的实例方法),并认为这可能是一种很好的方法来实现各种操作的超时.本地有超时,但支持取消.
但是看看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);
}
Run Code Online (Sandbox Code Playgroud)
假设我使用此方法为经常使用的基于TAP的操作提供超时,并用a包装它.CancelAfter().现在假设用户提供5分钟(300秒)的超时值,并且每秒调用此操作100次,这些操作在几毫秒后完成.在每秒100次呼叫300秒之后,即使任务很久以前成功完成,所有这些操作也将积累30,000个运行计时器.他们最终都会过去并运行上面的代表,这可能会抛出一个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);
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2930 次 |
| 最近记录: |