是CancellationTokenSource.CancelAfter()泄漏?(Is Cance

2019-06-24 16:54发布

在释放异步瞄准包促使我使用ILSpy来看看什么为基础的任务异步模式(TAP),在那里提供了扩展方法(其中一些我已经在我自己实施了使用VS2010)。 我偶然发现了.CancelAfter(TimeSpan)的方法CancellationTokenSource (这是在异步扩展方法靶向包.NET 4.0,但是在.NET 4.5的实例方法),并认为这可能是实现一个超时的好方法对于本身不具有超时,但支持取消各种操作。

但看在异步定位包的实现,看来,如果相关的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秒,3个运行定时器会从所有这些业务积累,即使任务圆满完成很久以前。 他们都经过最终并运行上述委托,这可能会抛出ObjectDisposedException等。

这不是有点漏水的,不可扩展的行为? 当我实现了一个暂停,我用Task/TaskEx.Delay(TimeSpan, CancellationToken)当相关的任务已经结束,我取消.Delay()这样的计时器将被停止并设置(这是一个IDisposable接口毕竟,而且它包含非托管资源)。 这是清理过于热心? 是具有同时运行(也可能抛出捕获的异常数万更高版本)真的无关紧要表现为平均应用数万定时器中的成本是多少? 是的开销和泄漏.CancelAfter()几乎总是比正在做的实际工作微不足道,并且通常应该被忽略?

Answer 1:

刚刚尝试它,把它推到极限,看看会发生什么。 我不能获得工作订走了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);
    }
}


文章来源: Is CancellationTokenSource.CancelAfter() leaky?