Является ли CancellationTokenSource.CancelAfter() дырявой?

Выпуск пакет Async Targeting Packпобудил меня использовать ILSpy, чтобы посмотреть, какие методы расширения Task-based Asynchronous Pattern (TAP)были там предоставлены (некоторые из которых Я уже сам реализовал для использования в VS2010) Наткнулся на .Метод CancelAfter(TimeSpan)для CancellationTokenSource(который является методом расширения в Async Targeting Pack для .NET 4.0, но является методом экземпляра в .NET 4.5) и подумал, что это может быть хорошим способом реализовать тайм-аут для различных операций, которые изначально не имеют тайм-аута, но поддерживают отмену.

Но глядя на реализацию в Async Targeting Pack, кажется, что если соответствующая задачазавершается или отменяется, таймер продолжает работать.

/// Cancels the  after the specified duration.
/// The CancellationTokenSource.
/// The due time in milliseconds for the source to be canceled.
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 раз в секунду, и все они успешно завершаются через несколько миллисекунд. Через 300 секунд 100 вызовов в секунду накопилось бы 30 000 запущенных таймеров от всех этих операций, даже если задачи были успешно завершены давно. Все они в конечном итоге завершатся и запустят приведенный выше делегат, который, вероятно, вызовет ObjectDisposedExceptionи т. д.

Разве это не какое-то дырявое, немасштабируемое поведение? Когда я реализовал тайм-аут, я использовал Task/TaskEx.Delay(TimeSpan, CancellationToken), и когда связанная задача завершилась, я отменил .Delay(), чтобы таймер быть остановлен и удален (в конце концов, это IDisposable, и он содержит неуправляемые ресурсы).Не слишком ли усердна эта уборка? Действительно ли стоимость десятков тысяч одновременно работающих таймеров (и, возможно, создание десятков тысяч перехваченных исключений) не имеет значения для производительности среднего приложения? Являются ли накладные расходы и утечка .CancelAfter()почти всегда незначительными по сравнению с фактической выполняемой работой, и их вообще следует игнорировать?

5
задан Allon Guralnek 23 May 2012 в 07:53
поделиться