web-dev-qa-db-fra.com

Liaison des jetons d'annulation

J'utilise un jeton d'annulation qui circule afin que mon service puisse être fermé proprement. Le service a une logique qui continue d'essayer de se connecter à d'autres services. Le jeton est donc un bon moyen de sortir de ces boucles de nouvelle tentative s'exécutant dans des threads distincts. Mon problème est que je dois appeler un service doté d'une logique de nouvelle tentative interne, mais le renvoyer après une période définie si une nouvelle tentative échoue. Je souhaite créer un nouveau jeton d'annulation avec un délai d'expiration qui le fera pour moi. Le problème, c'est que mon nouveau jeton n'est pas lié au jeton «maître». Par conséquent, lorsque le jeton principal est annulé, mon nouveau jeton sera toujours actif jusqu'à l'expiration du délai ou jusqu'à ce que la connexion soit établie et qu'elle retourne. Ce que je voudrais faire est de relier les deux jetons de telle sorte que lorsque le maître est annulé, mon nouveau sera également annulé. J'ai essayé d'utiliser la méthode CancellationTokenSource.CreateLinkedTokenSource, mais lorsque mon nouveau jeton a expiré, il a également annulé le jeton principal. Existe-t-il un moyen de faire ce que je dois faire avec les jetons ou cela nécessitera-t-il des modifications dans la logique de nouvelle tentative (ne sera probablement pas en mesure de le faire facilement)

Voici ce que je veux faire:

Jeton principal: transmis autour de diverses fonctions afin que le service puisse être arrêté proprement. Jeton temporaire: transmis à une seule fonction et défini pour expirer après une minute.

Si le jeton principal est annulé, le jeton temporaire doit également l'être.

Lorsque le jeton temporaire expire, il ne doit PAS annuler le jeton principal.

23
Retrocoder

Vous voulez utiliser CancellationTokenSource.CreateLinkedTokenSource. Cela permet d'avoir un "parent" et un "enfant" CancellationTokenSourcees. Voici un exemple simple:

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

Sortie comme prévu:

Annuler l'enfant CTS
Enfant CTS: vrai
Parent CTS: False 

Annuler le parent CTS
Enfant CTS: vrai
Parent CTS: True 

41
i3arnon

Comme i3arnon a déjà répondu , vous pouvez le faire avec CancellationTokenSource.CreateLinkedTokenSource(). Je souhaite essayer de montrer comment utiliser un tel jeton lorsque vous souhaitez faire la distinction entre l'annulation d'une tâche globale et l'annulation d'une tâche enfant sans annuler la tâche globale.

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)
        {
        }
    }
}

Lorsque le jeton temporaire expire, il ne doit PAS annuler le jeton principal.

Notez que la signature de MyAsyncTask() accepte CancellationToken au lieu de CancellationTokenSource. Etant donné que la méthode n'a accès qu'aux membres de CancellationToken, elle ne peut pas annuler accidentellement le jeton maître/parent. Je vous recommande d'organiser votre code de manière à ce que la variable CancellationTokenSource de la tâche principale soit visible avec le moins de code possible. Dans la plupart des cas, cela peut être fait en passant CancellationTokenSource.Token aux méthodes au lieu de partager la référence à CancellationTokenSource.

Je n'ai pas enquêté, mais il peut exister un moyen avec quelque chose comme une réflexion pour annuler de force une CancellationToken sans accès à sa CancellationTokenSource. J'espère que c'est impossible, mais si cela était possible, ce serait considéré comme une mauvaise pratique et ce n'est pas un sujet de préoccupation pour tous.

0
binki