Aufgabe nicht Müll gesammelt

Im folgenden Programm würde ich davon ausgehen, dass die Aufgabe gecodet wird, aber das ist nicht der Fall. Ich habe einen Speicherprofiler verwendet, der zeigte, dass dieCancellationTokenSource enthält einen Verweis darauf, obwohl die Aufgabe eindeutig abgeschlossen ist. Wenn ich entferneTaskContinuationOptions.OnlyOnRanToCompletionfunktioniert alles wie erwartet.

Warum passiert es und was kann ich tun, um es zu verhindern?

    static void Main()
    {
        var cts = new CancellationTokenSource();

        var weakTask = Start(cts);

        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();

        Console.WriteLine(weakTask.IsAlive); // prints True

        GC.KeepAlive(cts);
    }

    private static WeakReference Start(CancellationTokenSource cts)
    {
        var task = Task.Factory.StartNew(() => { throw new Exception(); });
        var cont = task.ContinueWith(t => { }, cts.Token, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Default);
        ((IAsyncResult)cont).AsyncWaitHandle.WaitOne(); // prevents inlining of Task.Wait()
        Console.WriteLine(task.Status); // Faulted
        Console.WriteLine(cont.Status); // Canceled
        return new WeakReference(task);
    }

Mein Verdacht ist, dass die Fortsetzung nie ausgeführt wird (sie erfüllt nicht die in den Optionen angegebenen Kriterien) und sich niemals vom Storno-Token abmeldet. Das CTS enthält also einen Verweis auf die Fortsetzung, der einen Verweis auf die erste Aufgabe enthält.

Aktualisieren

Das PFX-Team hat bestätigt, dass dies ein Leck zu sein scheint. Als Problemumgehung haben wir die Verwendung von Fortführungsbedingungen bei der Verwendung von Abbruchtoken eingestellt. Stattdessen führen wir immer die Fortsetzung aus, überprüfen die Bedingung im Inneren und werfen einOperationCanceledException wenn es nicht erfüllt ist. Dadurch bleibt die Semantik der Fortsetzung erhalten. Die folgende Erweiterungsmethode kapselt dies:

public static Task ContinueWith(this Task task, Func<TaskStatus, bool> predicate, 
    Action<Task> continuation, CancellationToken token)
{
    return task.ContinueWith(t =>
      {
         if (predicate(t.Status))
              continuation(t);
         else
              throw new OperationCanceledException();
      }, token);
}

Antworten auf die Frage(2)

Ihre Antwort auf die Frage