Verknüpfung von Stornierungstokens

Verknüpfung von Stornierungstokens

Sie möchten CancellationTokenSource.CreateLinkedTokenSource verwenden . Es erlaubt, einen "Elternteil" und ein "Kind" CancellationTokenSource zu haben es. Hier ist ein einfaches Beispiel:

var parentCts = new CancellationTokenSource();
var childCts = CancellationTokenSource.CreateLinkedTokenSource(parentCts.Token);

childCts.CancelAfter(1000);
Console.WriteLine("Cancel child CTS");
Thread.Sleep(2000);
Console.WriteLine("Child CTS: {0}", childCts.IsCancellationRequested);
Console.WriteLine("Parent CTS: {0}", parentCts.IsCancellationRequested);
Console.WriteLine();

parentCts.Cancel();
Console.WriteLine("Cancel parent CTS");
Console.WriteLine("Child CTS: {0}", childCts.IsCancellationRequested);
Console.WriteLine("Parent CTS: {0}", parentCts.IsCancellationRequested);

Ausgabe wie erwartet:


Wenn Sie nur einen CancellationToken haben , anstelle von CancellationTokenSource , dann ist es immer noch möglich, ein verknüpftes Stornierungstoken zu erstellen. Sie würden einfach den Register verwenden Methode zum Auslösen der Stornierung des (Pseudo-)Kindes:

var child = new CancellationTokenSource();
token.Register(child.Cancel);

Sie können alles tun, was Sie normalerweise mit einem CancellationTokenSource tun würden . Sie können es zum Beispiel nach einer bestimmten Dauer kündigen und sogar Ihr vorheriges Token überschreiben.

child.CancelAfter(cancelTime);
token = child.Token;

Wie i3arnon bereits geantwortet hat, können Sie dies mit CancellationTokenSource.CreateLinkedTokenSource() tun . Ich möchte versuchen, ein Muster zu zeigen, wie ein solches Token verwendet wird, wenn Sie zwischen dem Abbrechen einer Gesamtaufgabe und dem Abbrechen einer untergeordneten Aufgabe ohne Abbrechen der Gesamtaufgabe unterscheiden möchten.

async Task MyAsyncTask(
    CancellationToken ct)
{
    // Keep retrying until the master process is cancelled.
    while (true)
    {
        // Ensure we cancel ourselves if the parent is cancelled.
        ct.ThrowIfCancellationRequested();

        var childCts = CancellationTokenSource.CreateLinkedTokenSource(ct);
        // Set a timeout because sometimes stuff gets stuck.
        childCts.CancelAfter(TimeSpan.FromSeconds(32));
        try
        {
            await DoSomethingAsync(childCts.Token);
        }
        // If our attempt timed out, catch so that our retry loop continues.
        // Note: because the token is linked, the parent token may have been
        // cancelled. We check this at the beginning of the while loop.
        catch (OperationCancelledException) when (childCts.IsCancellationRequested)
        {
        }
    }
}

Beachten Sie, dass MyAsyncTask() Die Signatur von akzeptiert CancellationToken statt CancellationTokenSource . Da die Methode nur Zugriff auf die Mitglieder auf CancellationToken hat , kann es das Master-/Eltern-Token nicht versehentlich stornieren. Ich empfehle Ihnen, Ihren Code so zu organisieren, dass die CancellationTokenSource der Mastertask ist für so wenig Code wie möglich sichtbar. In den meisten Fällen kann dies durch Übergeben von CancellationTokenSource.Token erfolgen zu Methoden, anstatt den Verweis auf CancellationTokenSource zu teilen .

Ich habe es nicht untersucht, aber es könnte eine Möglichkeit geben, mit so etwas wie Reflektion einen CancellationToken zwangsweise abzubrechen ohne Zugriff auf seinen CancellationTokenSource . Hoffentlich ist es unmöglich, aber wenn es möglich wäre, würde es als schlechte Praxis angesehen werden und ist kein Grund zur Sorge.