2012-11-12 6 views
7

Sto tentando di annullare un'attività < chiamando il metodo CancellationTokenSource.Cancel() all'interno dell'attività, ma non riesco a farlo funzionare.Annullamento di un'attività all'interno di un'attività

Ecco il codice che sto usando:

TaskScheduler ts = TaskScheduler.Current; 

CancellationTokenSource cts = new CancellationTokenSource(); 

Task t = new Task(() => 
{ 
    Console.WriteLine("In Task"); 
    cts.Cancel(); 
}, cts.Token); 

Task c1 = t.ContinueWith(antecedent => 
{ 
    Console.WriteLine("In ContinueWith 1"); 
}, CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, ts); 

Task c2 = c1.ContinueWith(antecedent => 
{ 
    Console.WriteLine("In ContinueWith 2"); 
}, TaskContinuationOptions.NotOnCanceled); 

t.Start(); 

Console.ReadKey(); 

Environment.Exit(1); 

Questa stampa out:

In Task 
In ContinueWith 1 
In ContinueWith 2 

quello che mi aspettavo era questo:

In Task 

mi manca qualcosa qui? Le attività possono essere annullate solo all'esterno dello dell'attività?

+0

Penso, non capisci il principio di cancellazione. Vedi il metodo 'CancellationToken.ThrowIfCancellationRequested'. –

+0

Questo perché ho solo iniziato ad imparare TPL. – Intrepid

+0

Penso che questa sia davvero una buona domanda, è stato sorpreso di essere in grado di riprodurre. –

risposta

7

Un compito è considerato solo "annullata" se

  • suo token cancellazione viene annullata prima si inizia l'esecuzione.
  • sua cancellazione token viene annullata mentre è in esecuzione e il codice osserva in modo cooperativo la cancellazione gettando l'OperationCancelledException (di solito dal codice chiamando cts.Token.ThrowIfCancellationRequested())

Se è stato aggiunto una linea cts.Token.ThrowIfCancellationRequested() dopo la cts.Cancel() allora le cose si sarebbero comportati come ti aspetti. Nel tuo esempio, l'annullamento avviene mentre l'attività è in esecuzione, ma l'attività non osserva l'annullamento e l'azione dell'attività viene eseguita fino al completamento. Quindi l'attività è contrassegnata come "Ran to Completion".

È possibile verificare il caso di un'attività che "eseguiva il completamento" ma è stata annullata (durante o dopo il completamento dell'attività) controllando il token di cancellazione nel seguito (cts.Token.IsCancellationRequested). Un'altra opzione che a volte aiuta è quella di usare il token di annullamento originale come token di cancellazione per la continuazione (in questo modo, se l'annullamento non viene notato dal task antecedente, sarà comunque rispettato dal seguito) - dal momento che il TPL segnerà la continuazione come cancellata prima ancora che abbia la possibilità di correre.

+0

Accetterò la tua risposta perché ha senso. Sono anche riuscito a farlo funzionare come previsto utilizzando le informazioni fornite. – Intrepid

+0

Grande - grazie per aver segnalato. –

+0

@MattSmith: cosa intendi per 'token di annullamento originale come token di cancellazione per la continuazione? .. Ti dispiacerebbe fornire uno snippet di codice per esso modificando la risposta .. – dotNETbeginner

0

Questo frammento funziona come previsto.

var cts = new CancellationTokenSource(); 

      var t = new Task(() => 
      { 
       Console.WriteLine("In Task"); 
       cts.Cancel(); 
      }, cts.Token); 

      Task c1 = t.ContinueWith(antecedent => 
      { 
       Console.WriteLine("In ContinueWith 1"); 
      }, CancellationToken.None, TaskContinuationOptions.OnlyOnCanceled/* by definition from MSDN here must be NotOnCanceled*/, TaskScheduler.Current); 

      c1.ContinueWith(antecedent => 
      { 
       Console.WriteLine("In ContinueWith 2"); 
      }, TaskContinuationOptions.NotOnCanceled); 

      t.Start(); 
      Console.ReadKey(); 

      Environment.Exit(1); 

Ma sembra che ci sia il bug nell'enum TaskContinuationOptions.

NotOnCanceled Specifica che l'attività di proseguimento non deve essere pianificata se il suo antecedente è stato annullato. Questa opzione non è valida per le continuazioni multi-task .

OnlyOnCanceled Specifica che l'attività di continuazione deve essere pianificata solo se il suo antecedente è stato annullato. Questa opzione non è valida per continuazioni multi-task.

+0

Se commentate la riga 'cts.Cancel();' non esegue il secondo ContinueWith() come mi aspettavo. Mi aspettavo 'In ContinueWith 2' per essere visualizzato. – Intrepid