Qual è la differenza tra restituire vuoto e restituire un’attività?

Osservando vari esempi di CTP Async C # vedo alcune funzioni asincrone che restituiscono void , e altre che restituiscono l’Attività non generica. Riesco a capire perché restituire un Task è utile per restituire i dati al chiamante quando l’operazione asincrona è completata, ma le funzioni che ho visto che hanno un tipo restituito di Task non restituiscono mai alcun dato. Perché non restituire void ?

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

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

Un metodo contrassegnato come async può restituire void , Task o Task . Quali sono le differenze tra loro?

Un Task restituisce il metodo asincrono può essere atteso, e quando l’attività si completa offrirà un T.

È ansible attendere un Task restituisce il metodo asincrono e, al termine dell’attività, è pianificata la prosecuzione dell’attività.

Non è ansible attendere un metodo di restituzione asincrona void ; è un metodo “fuoco 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 esegui un gestore di eventi asincroni. L’evento si triggers, il conduttore esegue; nessuno “attenderà” l’attività restituita dal gestore di eventi perché i gestori di eventi non restituiscono attività, e anche se lo facessero, quale codice userebbe l’attività per qualcosa? In genere non è il codice utente che trasferisce il controllo al gestore in primo luogo.

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

Quali tipi di metodi possono essere await ? È ansible await un metodo di annullamento del vuoto?

No, non è ansible attendere un metodo di restituzione del vuoto. Il compilatore traduce await M() in una chiamata a M().GetAwaiter() , dove GetAwaiter potrebbe essere un metodo di istanza o un metodo di estensione. Il valore atteso deve essere quello per cui è ansible ottenere un cameriere; chiaramente un metodo di ritorno del vuoto non produce un valore dal quale è ansible ottenere un cameriere.

I metodi di rielaborazione dei compiti possono produrre valori attendibili. Anticipiamo che le terze parti vorrebbero creare le proprie implementazioni di oggetti simili a Task che possono essere attesi, e sarete in grado di aspettarli. Tuttavia, non ti sarà consentito dichiarare metodi async che restituiscono nulla ma void , Task o Task .

(AGGIORNAMENTO: la mia ultima frase potrebbe essere falsificata 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 funzione sopra menzionata è stata inserita in C # 7).

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

Infatti, l’unico motivo per restituire void è se non è ansible restituire l’ Task perché si sta scrivendo un gestore di eventi.

I metodi che restituiscono Task e Task sono componibili, il che significa che puoi await all’interno di un metodo async .

async metodi async che restituiscono il void non 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 il conteggio delle operazioni asincrone in sospeso.

Il contesto ASP.NET è uno di questi contesto; se si utilizzano i metodi di Task asincrone senza attendere da un metodo di void asincrono, la richiesta ASP.NET verrà completata troppo presto.

Un altro contesto è AsyncContext ho scritto per il test delle unità (disponibile qui ): il metodo AsyncContext.Run tiene traccia del conteggio delle operazioni in sospeso e restituisce quando è zero.

Tipo Task è il tipo di workhorse della Task Parallel Library (TPL), rappresenta il concetto di “qualche lavoro / lavoro che produrrà un risultato di tipo T in futuro”. Il concetto di “lavoro che verrà completato in futuro ma non restituisce alcun risultato” è rappresentato dal tipo di attività non generico.

Esattamente come il risultato del tipo T sta per essere prodotto e i dettagli di implementazione di un particolare compito; il lavoro può essere estratto in un altro processo sul computer locale, in un altro thread, ecc. Le attività TPL vengono tipicamente generate ai thread di lavoro da un pool di thread nel processo corrente, ma i dettagli dell’implementazione non sono fondamentali per l’ Task tipo; piuttosto una Task può rappresentare qualsiasi operazione ad alta latenza che produce una T

In base al tuo commento sopra:

L’espressione await significa “valutare questa espressione per ottenere un object che rappresenta un lavoro che in futuro produrrà un risultato. Iscriviti al resto del metodo corrente come la richiamata associata alla continuazione di tale attività. Una volta che l’attività è stata prodotta e la chiamata indietro è registrato, restituire immediatamente il controllo al mio chiamante “. Questo è opposto / in contrasto con una normale chiamata di metodo, che significa “ricorda cosa stai facendo, esegui questo metodo fino a quando non è completamente finito e riprendi da dove hai interrotto, ora conoscendo il risultato del metodo”.


Edit: Dovrei citare l’articolo di Eric Lippert nell’ottobre 2011 di 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.