Czy CancellationTokenSource.CancelAfter () jest nieszczelny?

WydanieAsync Targeting Pack skłonił mnie do użyciaILSpy rzucić okiem na coAsynchroniczny wzorzec zadań (TAP) udostępniono tam metody rozszerzeń (niektóre z nich już zaimplementowałem samodzielnie do użytku w VS2010). Natknąłem się na.CancelAfter(TimeSpan) metoda dlaCancellationTokenSource (jest to metoda rozszerzenia w Async Targeting Pack dla .NET 4.0, ale jest to metoda instancji w .NET 4.5) i pomyślał, że może to być miły sposób na wdrożenie limitu czasu dla różnych operacji, które nie mają natywnego limitu czasu, ale anuluj wsparcie.

Ale patrząc na implementację w pakiecie Async Targeting Pack, wydaje się, że jeśli jest powiązanyTask kończy się lub zostaje anulowany, zegar nadal działa.

/// <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);
}

Powiedzmy, że używam tej metody do zapewnienia limitu czasu dla często używanej operacji opartej na TAP i zawijam ją za pomocą.CancelAfter(). Powiedzmy, że użytkownik podaje wartość limitu czasu wynoszącą 5 minut (300 sekund) i wywołuje tę operację 100 razy na sekundę, które zakończą się pomyślnie po kilku milisekundach. Po 300 sekundach 100 wywołań na sekundę ze wszystkich tych operacji zgromadziłoby się 30 000 uruchomionych timerów, nawet jeśli zadania zakończyły się pomyślnie już dawno temu. Wszyscy w końcu upłyną i uruchomią powyższego delegata, który prawdopodobnie rzuciObjectDisposedExceptionitd.

Czy to nie jest nieszczelne, nieskalowalne zachowanie? Kiedy wdrożyłem limit czasu, użyłemTask/TaskEx.Delay(TimeSpan, CancellationToken) a gdy powiązane zadanie zakończy się, anuluję.Delay() tak, aby timer został zatrzymany i usunięty (w końcu jest to IDisposable i zawiera niezarządzane zasoby). Czy to sprzątanie jest zbyt gorliwe? Czy koszt posiadania kilkudziesięciu tysięcy zegarów działających jednocześnie (i prawdopodobnie wyrzucających dziesiątki tysięcy późniejszych wyjątków) naprawdę nie ma znaczenia dla wydajności przeciętnej aplikacji? Czy koszty ogólne i nieszczelność.CancelAfter() prawie zawsze maleńka w porównaniu z rzeczywistą wykonywaną pracą i generalnie należy ją lekceważyć?

questionAnswers(1)

yourAnswerToTheQuestion