2015-04-14 13 views
14

Uso un token di cancellazione che viene passato in giro in modo che il mio servizio possa essere chiuso in modo pulito. Il servizio ha una logica che continua a provare a connettersi ad altri servizi, quindi il token è un buon modo per uscire da questi tentativi di loop in esecuzione in thread separati. Il mio problema è che ho bisogno di effettuare una chiamata a un servizio che ha una logica interna per i tentativi, ma di tornare dopo un periodo prestabilito se un tentativo fallisce. Vorrei creare un nuovo token di cancellazione con un timeout che farà questo per me. Il problema con questo è che il mio nuovo token non è collegato al token "master", quindi quando il token master viene cancellato, il mio nuovo token sarà ancora vivo fino a quando non viene eseguito il timeout o la connessione viene restituita. Quello che mi piacerebbe fare è collegare i due token insieme in modo che quando si cancella quello principale il mio nuovo annullerà anche. Ho provato a utilizzare il metodo CancellationTokenSource.CreateLinkedTokenSource ma quando il mio nuovo token è scaduto, ha anche annullato il token master. C'è un modo per fare quello che devo fare con gettoni o intende richiedere modifiche alla logica di tentativi (probabilmente non sarà in grado di farlo facilmente)Collegamento dei token di cancellazione

Ecco cosa voglio fare:

Master Token - ha distribuito varie funzioni in modo che il servizio possa essere chiuso in modo pulito. Token temporaneo - passato a una singola funzione e impostato su timeout dopo un minuto

Se il token master viene cancellato, anche il token temporaneo deve essere annullato.

Quando il token temporaneo scade, NON deve annullare il token master.

risposta

17

Si desidera utilizzare CancellationTokenSource.CreateLinkedTokenSource. Permette di avere un "genitore" e un "figlio" CancellationTokenSource es. Ecco un semplice esempio:

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

uscita come previsto:

Annulla bambino CTS
Bambino CTS: Vero
Parent CTS: False

Annulla genitore CTS
Bambino CTS: Vero
Parent CTS: True

+2

Sei corretto. Ho iniziato a utilizzare CancellationTokenSource.CreateLinkedTokenSource ma ho pensato che non funzionasse. Ho dimenticato che quando il token va in time genera un'eccezione. Questo è stato preso più in alto nel mio codice. Questo ha dato l'impressione che non funzionasse come mi aspettavo. Inserendo la mia chiamata in un blocco catch try ha funzionato bene. – Retrocoder

+0

@Retrocoder Se si desidera solo intrappolare il token interno, si consiglia di utilizzare un modello come 'try {doSomething (ct: childCts.Token); } catch (OperationCancelledException) quando (childCts.IsCancellationRequested) {} ​​'. È possibile inserirlo all'interno di un ciclo di tentativi e creare la sorgente di codice figlio all'interno del ciclo. Quindi, quando il gettone genitore viene cancellato, salirà a bolla, ma quando il segnalino figlio annulla, fa semplicemente un tentativo. Non posso dire dal tuo commento, potresti già farlo correttamente ;-). – binki

0

Come i3arnon already answered, è possibile farlo con CancellationTokenSource.CreateLinkedTokenSource(). Voglio provare a mostrare un modello di come utilizzare un tale token quando si desidera distinguere tra l'annullamento di un'attività globale e l'annullamento di un'attività figlio senza annullamento dell'attività generale.

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

Quando il Temporary Token scadenza non deve annullare il Maestro Token.

Si noti che la firma MyAsyncTask() s’accetta CancellationToken piuttosto che CancellationTokenSource. Poiché il metodo ha accesso solo ai membri su CancellationToken, non può annullare accidentalmente il token principale/genitore. Raccomando di organizzare il codice in modo tale che lo CancellationTokenSource dell'attività principale sia visibile con il minor numero possibile di codice. Nella maggior parte dei casi, questo può essere fatto passando lo CancellationTokenSource.Token ai metodi invece di condividere il riferimento allo CancellationTokenSource.

Non ho indagato, ma ci può essere un modo con qualcosa come il riflesso per cancellare forzatamente uno CancellationToken senza accedere al suo CancellationTokenSource. Speriamo che sia impossibile, ma se fosse possibile, sarebbe considerato una cattiva pratica e non è qualcosa di cui preoccuparsi in generale.

Problemi correlati