Qual è la differenza tra la restituzione del nulla e la restituzione di un'attività?


128

Guardando vari esempi CTP asincroni C # vedo alcune funzioni asincrone che restituiscono voide altre che restituiscono il non generico Task. Posso capire perché la restituzione di a Task<MyType>è utile per restituire i dati al chiamante al termine dell'operazione asincrona, ma le funzioni che ho visto che hanno un tipo di Taskrestituzione non restituiscono mai alcun dato. Perché non tornare void?

Risposte:


214

Le risposte di SLaks e Killercam sono buone; Ho pensato di aggiungere solo un po 'più di contesto.

La tua prima domanda riguarda essenzialmente quali metodi possono essere contrassegnati async.

Un metodo contrassegnato come asyncpuò restituire void, Taskoppure Task<T>. Quali sono le differenze tra loro?

È Task<T>possibile attendere un metodo asincrono di ritorno e quando l'attività viene completata, viene offerto un T.

È Taskpossibile attendere un metodo asincrono di ritorno e al termine dell'attività è pianificata l'esecuzione della continuazione dell'attività.

Un voidmetodo asincrono ritorno non può essere atteso; è un metodo "spara e dimentica". Funziona in modo asincrono e non hai modo di dire quando è fatto. Questo è più che un po 'strano; come dice SLaks, normalmente lo faresti solo quando crei un gestore di eventi asincrono. L'evento si attiva, il gestore esegue; nessuno "attenderà" l'attività restituita dal gestore eventi perché i gestori eventi non restituiscono attività e, anche se lo facessero, quale codice userebbe l'attività per qualcosa? Di solito non è il codice utente che trasferisce il controllo al gestore.

La tua seconda domanda, in un commento, riguarda essenzialmente ciò che può essere awaited:

Quali tipi di metodi possono essere modificati await? È possibile modificare un metodo di ritorno del vuoto await?

No, non è possibile attendere un metodo di ritorno del vuoto. Il compilatore si traduce await M()in una chiamata a M().GetAwaiter(), dove GetAwaiterpotrebbe essere un metodo di istanza o un metodo di estensione. Il valore atteso deve essere uno per il quale è possibile ottenere un cameriere; chiaramente un metodo di ritorno del vuoto non produce un valore da cui è possibile ottenere un cameriere.

Taski metodi di ritorno possono produrre valori attendibili. Prevediamo che terze parti vorranno creare le proprie implementazioni di Taskoggetti simili che possono essere attesi e che sarete in grado di aspettarli. Tuttavia, non ti sarà permesso di dichiarare asyncmetodi che restituiscono altro che void, Tasko Task<T>.

(AGGIORNAMENTO: la mia ultima frase potrebbe essere falsata da una versione futura di C #; esiste una proposta per consentire tipi di ritorno diversi dai tipi di attività per i metodi asincroni.)

(AGGIORNAMENTO: la funzionalità sopra menzionata è arrivata a C # 7.)


7
+1 Penso che l'unica cosa che manca sia la differenza nel modo in cui le eccezioni vengono trattate nei metodi asincroni che restituiscono il vuoto.
João Angelo,

10
@JamesCadd: Supponiamo che alcuni lavori asincroni generino un'eccezione. Chi lo prende? Il codice che ha avviato l'attività asincrona non è più nello stack - potrebbe non essere nemmeno sullo stesso thread - e le eccezioni presuppongono che tutti i blocchi catch / finally siano nello stack . Allora cosa fai? Archiviamo le informazioni sull'eccezione nell'attività, in modo da poterle ispezionare in un secondo momento. Ma se il metodo non viene restituito, non è disponibile alcuna attività per il codice utente. Il modo in cui affrontiamo esattamente questa situazione è stato oggetto di alcune controversie e in questo momento non ricordo cosa abbiamo deciso.
Eric Lippert,

8
In realtà ho posto questa domanda a Stephen Toub alla BUILD. In .NET 4.0 le eccezioni non osservate e non gestite nelle Attività finirebbero per arrestare il processo una volta che TPL rileva che non sono state osservate. In 4.5 hanno modificato il comportamento predefinito in modo che le eccezioni non osservate vengano comunque segnalate tramite l'evento TaskScheduler :: UnobservedTaskException, ma non arrestano più il processo. Se si desidera il vecchio comportamento 4.0, è possibile riattivare con <runtime> <ThrowUnobservedTaskExceptions enabled = "true" /> </runtime>. Molto probabilmente la modifica è stata apportata proprio per supportare i metodi asincroni nulli.
Ha disegnato Marsh il

4
async voidi metodi sollevano la loro eccezione su SynchronizationContextciò che era attivo nel momento in cui hanno iniziato l'esecuzione. Questo è simile al comportamento dei gestori di eventi (sincroni). @DrewMarsh: l' UnobservedTaskExceptione runtime impostazione applica solo ai "fire and forget" async Task metodi, non async voidi metodi.
Stephen Cleary,

1
Link di citazione per informazioni sulla gestione delle eccezioni asincrone: blogs.msdn.com/b/pfxteam/archive/2012/04/12/10293335.aspx#11
Luke Puplett

23

Nel caso in cui il chiamante desideri attendere l'attività o aggiungere una continuazione.

In effetti, l'unico motivo per tornare voidè se non puoi tornare Taskperché stai scrivendo un gestore di eventi.


Ho pensato che fosse possibile attendere metodi che restituissero anche un tipo vuoto - potresti elaborare un po '?
James Cadd,

1
No, non puoi. Se il metodo ritorna void, non hai modo di raggiungere l'attività che genera. (A dire il vero, non sono sicuro che generi nemmeno Taska)
SLaks

18

I metodi che ritornano Taske Task<T>sono compostabili, il che significa che puoi awaitfarli all'interno di un asyncmetodo.

asynci metodi di ritorno voidnon sono componibili, ma hanno altre due proprietà importanti:

  1. Possono essere usati come gestori di eventi.
  2. Rappresentano un'operazione asincrona di "livello superiore".

Il secondo punto è importante quando si ha a che fare con un contesto che mantiene un conteggio delle operazioni asincrone in sospeso.

Il contesto ASP.NET è uno di questi contesti; se si utilizzano Taskmetodi asincroni senza attenderli da un voidmetodo asincrono , la richiesta ASP.NET verrà completata troppo presto.

Un altro contesto è quello che AsyncContextho scritto per i test unitari (disponibile qui ): il AsyncContext.Runmetodo tiene traccia del conteggio delle operazioni in sospeso e ritorna quando è zero.


12

Tipo Task<T>è il tipo di cavallo di battaglia della Task Parallel Library (TPL), rappresenta il concetto di "lavoro / lavoro che produrrà un risultato di tipo Tin futuro". Il concetto di "lavoro che verrà completato in futuro ma non restituisce alcun risultato" è rappresentato dal tipo di attività non generico.

Esattamente come Tverrà prodotto il risultato del tipo e i dettagli di implementazione di un particolare compito; il lavoro potrebbe essere trasferito a un altro processo sul computer locale, a un altro thread, ecc. Le attività TPL vengono in genere trasferite ai thread di lavoro da un pool di thread nel processo corrente, ma i dettagli di implementazione non sono fondamentali per il Task<T>tipo; piuttosto a Task<T>può rappresentare qualsiasi operazione ad alta latenza che produce a T.

Sulla base del tuo commento sopra:

L' awaitespressione significa "valuta questa espressione per ottenere un oggetto che rappresenti lavoro che in futuro produrrà un risultato. Iscriviti al resto del metodo corrente come richiamata associata alla continuazione di quell'attività. Una volta che quell'attività viene prodotta e la richiamata è registrato, restituisce immediatamente il controllo al mio chiamante ". Questo è contrario / in contrasto con una normale chiamata di metodo, che significa "ricorda cosa stai facendo, esegui questo metodo fino a quando non è completamente finito e poi riprendi da dove avevi interrotto, ora conoscendo il risultato del metodo".


Modifica: dovrei citare l'articolo di Eric Lippert nell'ottobre 2011 su MSDN Magazine in quanto questo mi è stato di grande aiuto nella comprensione di queste cose in primo luogo.

Per ulteriori informazioni e pagine bianche vedi qui .

Spero che questo sia di qualche aiuto.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.