2010-08-11 1 views
22

Attualmente sto sostituendo qualche casa al forno funzionalità operazione con una nuova implementazione utilizzando la funzionalità nuove System.Threading.Tasks trovato in .net 4.Come ottenere la notifica che uno System.Threading.Tasks.Task ha completato

Tuttavia, ho un piccolo problema e, anche se posso pensare ad alcune soluzioni, vorrei ricevere qualche consiglio su quale sia in genere il modo migliore per farlo, e se mi manca un trucco da qualche parte.

Quello che mi serve è che un processo arbitrario sia in grado di avviare un'attività, ma poi proseguire e non attendere il termine dell'attività. Non è un problema, ma quando poi ho bisogno di fare qualcosa con il risultato di un compito non sono del tutto sicuro del modo migliore di farlo.

Tutti gli esempi che ho visto utilizzano Wait() sull'attività finché non completa o fa riferimento al parametro Result sull'attività. Entrambi blocceranno la discussione che ha avviato l'attività, che non desidero.

Alcune soluzioni che ho pensato:

  1. Creare un nuovo thread e avviare l'operazione su questo, quindi utilizzare Wait() o .Result per bloccare il nuovo thread e sincronizzare il risultato al chiamante in qualche modo, possibilmente con il polling al parametro IsCompleted delle attività.

  2. Avere un'attività "Notifica completata" che è possibile avviare dopo il completamento dell'attività che si desidera eseguire, quindi genera un evento statico o qualcosa del genere.

  3. Passare un delegato nell'input dell'attività e chiamarlo per notificare che l'attività è terminata.

posso pensare o di pro e contro a tutti loro, ma soprattutto non mi piace l'idea di dover creare esplicitamente un nuovo thread per avviare l'attività quando l'uno degli obiettivi di utilizzo del La classe di attività in primo luogo è quella di astrarre dall'uso diretto del thread.

Qualche idea sul modo migliore? Mi sto perdendo qualcosa di semplice? Un evento "Completato" sarebbe troppo chiedere :)? (Che ci sia una buona ragione per cui non ce n'è uno!)

+0

Per curiosità, perché non utilizzare BackgroundWorker? Ha il completamento del compito, così come la notifica di cancellazione. – Robaticus

+1

'BackgroundWorker' non è più necessario ora che' Task' è con noi. BGW richiedeva un 'SynchronizationContext', che è opzionale con' Task' (abilitando un maggiore parallelismo). –

risposta

26

ho sospetto siete alla ricerca di Task.ContinueWith (o Task<T>.ContinueWith). Questi sostanzialmente dicono "Quando hai finito questa attività, esegui questa azione". Tuttavia, ci sono varie opzioni che puoi specificare per avere più controllo su di esso.

MSDN fornisce ulteriori dettagli su questo in "How to: Chain Multiple Tasks With Continuations" e "Continuation Tasks".

+0

Sì, lo capisco, ma non risolve il problema di sapere quando la sequenza di Attività è terminata senza chiamare Task.Wait() (e quindi il blocco) sul thread che ha avviato l'attività. Un approccio che ho pensato (come sopra) era quello di utilizzare Task.ContinueWith per concatenare un'attività che genera un evento a cui le parti interessate possono iscriversi. Ciò manterrà l'attività originale atomica. –

+1

@bwilliams: Perché non esporre l'attività e le parti interessate possono chiamare 'Task.ContinueWith'? Non vedo in che modo * non * risolva il problema di sapere quando l'attività è stata completata ... è una chiamata non bloccante che dice "Richiamami quando è finita". –

+1

(O invece di esporre l'attività stessa, puoi fornire il tuo metodo che chiama semplicemente "ContinueWith" internamente.E 'la stessa cosa, davvero.) –

4

è possibile applicare una task continuation.

In alternativa, Task implementa IAsyncResult, in modo da poter utilizzare il standard approaches for that interface (blocco, polling, o in attesa sul suo WaitHandle).

+0

Per l'argomento di continuazione dell'attività si prega di vedere il mio commento sotto il post di Jon Skeet. Blocco e polling Non mi piace l'idea di come vorrà dire dover iniziare un altro thread per mantenere il thread originale che ha avviato il task responsive. Ho visto che potrei usare WaitHandle, ma la documentazione dice che il modo migliore è usare Task.Wait(). Detto questo, dato che non fa quello che voglio, forse il WaitHandle è il migliore per me –

4

Nella moderna C#, uno non ha più bisogno di chiamare in modo esplicito ContinueWith(). Un'alternativa alla risposta accettata originale sarebbe quello di creare semplicemente un metodo di async che await s il Task in questione, e fa tutto quello che vuole, quando la Task è completa.

Ad esempio, si supponga di voler generare un evento chiamato TaskCompleted quando il Task viene completato. Scriverebbe un metodo come:

async Task RaiseEventWhenTaskCompleted(Task task) 
{ 
    await task; 
    TaskCompleted?.Invoke(this, EventArgs.Empty); 
} 

Per "registrare" l'attesa, basta chiamare il metodo sopra. Aggiungere la gestione delle eccezioni come desiderato, nel metodo sopra, o in un codice che alla fine osserverà il Task restituito dal metodo precedente.