2015-09-15 5 views
9

Ho appena incontrato un po 'di codice come:utilizza un lambda `async` con` Task.Run() `ridondante?

var task = Task.Run(async() => { await Foo.StartAsync(); }); 
task.Wait(); 

(No, non so il funzionamento interno di Foo.StartAsync()). La mia reazione iniziale sarebbe sbarazzarsi di async/await e riscrivere come:

var task = Foo.StartAsync(); 
task.Wait(); 

Vorrei che sia corretto, o no (ancora una volta, senza sapere nulla affatto di Foo.StartAsync()). This la risposta a What difference does it make - running an 'async' action delegate with a Task.Run ... sembra indicare che ci possono essere casi in cui potrebbe avere senso, ma dice anche "Per dire la verità, non ho visto molti scenari ..."

+2

In entrambi i casi è necessario "attendere" l'attività anziché attendere in modo sincrono con "Task.Wait". – i3arnon

risposta

10

Normalmente, il destinato utilizzo per Task.Run è quello di eseguire codice CPU-bound su un filo non-UI. Come tale, sarebbe abbastanza raro che fosse usato con un delegato async, ma è possibile (ad es. Per il codice che ha parti asincrone e legate alla CPU).

Tuttavia, questo è l'utilizzo previsto. Credo che nel tuo esempio:

var task = Task.Run(async() => { await Foo.StartAsync(); }); 
task.Wait(); 

E 'molto più probabile che l'autore originale sta tentando di bloccare in modo sincrono su codice asincrono, ed è (ab) utilizzando Task.Run-avoid deadlocks common in that situation (come ho descritto nel mio blog).

In sostanza, sembra il "thread pool hack" che descrivo nel mio article on brownfield asynchronous code.

La soluzione migliore è di non usare Task.RunoWait:

await Foo.StartAsync(); 

Questo farà sì che async a crescere attraverso il vostro codice di base, che è l'approccio migliore, ma può causare una quantità inaccettabile di lavoro per i tuoi sviluppatori in questo momento. Questo è presumibilmente il motivo per cui il tuo predecessore ha usato Task.Run(..).Wait().

+2

La nozione di "async' grow [ing] tramite il tuo codice base" suona simile a ciò che accade quando si introduce 'IDisposable' da qualche parte (mentre si fa il passo alla domanda se che l'uso di 'IDisposable' è corretto). –

+2

@Dan: Sì, ci sono problemi di progettazione molto simili condivisi tra 'IDisposable' e' async'. –

4

Principalmente sì.

Utilizzare Task.Run come questo è utilizzato principalmente da persone che non capiscono come eseguire un metodo asincrono.

Tuttavia, c'è una differenza. Usare Task.Run significa avviare il metodo asincrono su un thread ThreadPool.

Ciò può essere utile quando la parte sincrona del metodo asincrono (la parte prima della prima attesa) è notevole e il chiamante desidera assicurarsi che il metodo non stia bloccando.

Questo può anche essere utilizzato per "uscire" dal contesto corrente, ad esempio dove non c'è uno SynchronizationContext.

+0

Come se l'esecuzione di 'Foo.StartAsync()' su un thread ThreadPool fosse importante, ci si aspetterebbe un commento per quell'effetto? –

+1

@Dan non lo direi. Inoltre, è meno un problema per il metodo stesso. Potrebbe essere un problema per il chiamante.Se il tuo thread è "speciale" (thread dell'interfaccia utente, o in attesa di richieste) e vuoi scaricare il più possibile su altri thread, puoi usare "Task.Run". – i3arnon

+0

quindi il modo consigliato dovrebbe essere asincrono da attendere all'interno di Task.Run pure? –