2014-09-02 6 views
9

Quindi, task.Wait() può essere convertito in await task. La semantica è diversa, ovviamente, ma questo è più o meno come farei per trasformare un codice di blocco con Waits in un codice asincrono con awaits.Come trasformare task.Wait (CancellationToken) in una dichiarazione di attesa?

La mia domanda è come trasformare task.Wait(CancellationToken) nella rispettiva istruzione await?

+0

possibile duplicato di [token di cancellazione in attesa metodo] (http://stackoverflow.com/questions/13822726/cancellation-token-in-await-method) –

+4

No, ho visto quello e non ho trovato la risposta Là. Per favore, rimuovi l'indicatore di duplicazione, a meno che non sia spiegato perché sono duplicati. – mark

risposta

6

Per creare un nuovo Task che rappresenta un'attività esistente ma con un token di annullamento aggiuntivo è piuttosto semplice. Devi solo chiamare ContinueWith sull'attività, utilizzare il nuovo token e propagare il risultato/le eccezioni nel corpo della continuazione.

public static Task WithCancellation(this Task task, 
    CancellationToken token) 
{ 
    return task.ContinueWith(t => t.GetAwaiter().GetResult(), token); 
} 
public static Task<T> WithCancellation<T>(this Task<T> task, 
    CancellationToken token) 
{ 
    return task.ContinueWith(t => t.GetAwaiter().GetResult(), token); 
} 

Questo consente di scrivere task.WithCancellation(cancellationToken) per aggiungere un token per un compito, che si può quindi await.

+0

Perché non usare 't.Result' ?Quindi l'implementazione sarebbe la stessa sia per 'Task ' che 'Task'? – i3arnon

+0

@ I3arnon 'Result' non ha la semantica corretta di propagazione degli errori. Ma la simmetria è bella, sì. – Servy

+0

@Servy - ottima risposta. Non mi sarei mai aspettato che passare il token di cancellazione alla continuazione annullasse effettivamente il compito stesso. – mark

8

await viene utilizzato per i metodi asincroni/delegati, che sia accettano un CancellationToken e così si dovrebbe passare uno quando si chiama (cioè await Task.Delay(1000, cancellationToken)), o non lo fanno e non può davvero essere cancellate (ad esempio in attesa di un risultato I/O).

Cosa si può fare però, è abbandonare * questi tipi di attività con questo metodo di estensione:

public static Task<T> WithCancellation<T>(this Task<T> task, CancellationToken cancellationToken) 
{ 
    return task.IsCompleted // fast-path optimization 
     ? task 
     : task.ContinueWith(
      completedTask => completedTask.GetAwaiter().GetResult(), 
      cancellationToken, 
      TaskContinuationOptions.ExecuteSynchronously, 
      TaskScheduler.Default); 
} 

Usage:

await task.WithCancellation(cancellationToken); 

* Il compito abbandonata non ottiene cancellato, ma il tuo codice si comporta come se lo fosse. Termina con un risultato/eccezione o rimarrà in vita per sempre.

+0

Wow, questo è inaspettatamente coinvolto. – mark

+2

@mark Questo perché 'async-await' richiede una mentalità diversa. attività di delega "regolari" e attività "asincrone" promettenti, sebbene condividano un tipo, sono concetti piuttosto diversi ... [Due tipi di attività] (http://blog.stephencleary.com/2014/04/a-tour- of-task-part-0-overview.html) – i3arnon

+1

+1 per una soluzione elegante. Ho un metodo di estensione praticamente identico nella mia base di codice, con l'eccezione che mi preoccupo solo di registrare una richiamata di cancellazione se 'cancellationToken.CanBeCanceled' restituisce' true'. –

Problemi correlati