Task.Result è uguale a .GetAwaiter.GetResult ()?


328

Recentemente stavo leggendo del codice che utilizza molti metodi asincroni, ma a volte ha bisogno di eseguirli in modo sincrono. Il codice fa:

Foo foo = GetFooAsync(...).GetAwaiter().GetResult();

È lo stesso di

Foo foo = GetFooAsync(...).Result;

8
Dai documenti di GetResult: "Questo tipo e i suoi membri sono destinati all'uso da parte del compilatore." L'altra persona non dovrebbe usarlo.
spender

32
Questo si chiama "sincronizzazione over asincrona", e se non si sa come l'operazione è implementata può essere una davvero pessima idea. In molti casi può verificarsi un deadlock istantaneo (un metodo async/ awaitin MVC, ad esempio)
Marc Gravell


14
Nel mondo reale, abbiamo costruttori, non abbiamo interfacce "non attendono" che dobbiamo implementare e ci vengono dati metodi asincroni ovunque. Sarei lieto di usare qualcosa che funziona senza di me. Mi chiedo perché sia ​​"pericoloso", "da non usare" o "da evitare a tutti i costi". Ogni volta che devo fare casino con Async mi viene il mal di testa.
Larry,

Risposte:


173

Abbastanza. Una piccola differenza però: se il Taskfallimento fallisce, GetResult()genererà direttamente l'eccezione causata direttamente, mentre Task.Resultgenererà un AggregateException. Tuttavia, qual è lo scopo di utilizzare uno di quelli quando è async? L'opzione 100x migliore è da usare await.

Inoltre, non sei destinato a utilizzare GetResult(). È pensato solo per l'uso del compilatore, non per te. Ma se non vuoi il fastidioso AggregateException, usalo .


27
@JayBazuzi Non se il tuo framework di unit test supporta i unit test asincroni, cosa che penso facciano con le versioni più recenti della maggior parte dei framework.
svick,

15
@JayBazuzi: MSTest, xUnit e NUnit supportano tutti i async Tasktest delle unità e sono già da un po 'di tempo.
Stephen Cleary,

18
spingendo indietro su 100x - è 1000x peggio di usare wait se stai adattando il vecchio codice e l'utilizzo di wait richiede una riscrittura.
bloccato il

13
@AlexZhukovskiy: non sono d'accordo .
Stephen Cleary,

15
The 100x better option is to use await.Odio le affermazioni come questa, se potessi schiaffeggiarla await, lo farei. Ma, quando sto cercando di ottenere il codice asincrono al lavoro contro il codice non asincrone come succede spesso a me un sacco di Xamarin, finisco per dover usare cose come ContinueWithun sacco in modo da rendere non deadlock l'interfaccia utente. Modifica: so che questo è vecchio, ma ciò non allevia la mia frustrazione nel trovare risposte che lo affermino senza alternative per situazioni in cui non puoi semplicemente usare await.
Thomas F.

147

Task.GetAwaiter().GetResult()è preferito Task.Waite Task.Resultperché propaga le eccezioni piuttosto che racchiuderle in un file AggregateException. Tuttavia, tutti e tre i metodi causano il potenziale per deadlock e problemi di fame del pool di thread. Tutti dovrebbero essere evitati a favore di async/await.

La citazione seguente spiega perché Task.Waite Task.Resultnon contiene semplicemente il comportamento di propagazione delle eccezioni di Task.GetAwaiter().GetResult()(a causa di una "barra di compatibilità molto elevata").

Come accennato in precedenza, abbiamo una barra di compatibilità molto elevata e quindi abbiamo evitato di interrompere le modifiche. Come tale, Task.Waitmantiene il suo comportamento originale di sempre avvolgente. Tuttavia, potresti trovarti in alcune situazioni avanzate in cui desideri un comportamento simile al blocco sincrono impiegato Task.Wait, ma in cui desideri che l'eccezione originale venga propagata da scartare anziché essere racchiusa in un AggregateException. Per raggiungere questo obiettivo, puoi indirizzare direttamente il cameriere dell'attività. Quando scrivi “ await task;”, il compilatore lo traduce in uso del Task.GetAwaiter()metodo, che restituisce un'istanza che ha un GetResult()metodo. Se utilizzato su un'attività con errori, GetResult()propaga l'eccezione originale (in questo modo " await task;" ottiene il suo comportamento). Puoi quindi utilizzare "task.GetAwaiter().GetResult()"Se si desidera invocare direttamente questa logica di propagazione.

https://blogs.msdn.microsoft.com/pfxteam/2011/09/28/task-exception-handling-in-net-4-5/

" GetResult" In realtà significa "controllare l'attività per errori"

In generale, faccio del mio meglio per evitare il blocco sincrono su un'attività asincrona. Tuttavia, ci sono alcune situazioni in cui viola tale linea guida. In quelle rare condizioni, il mio metodo preferito è GetAwaiter().GetResult()perché conserva le eccezioni dell'attività anziché racchiuderle in un file AggregateException.

http://blog.stephencleary.com/2014/12/a-tour-of-task-part-6-results.html


3
Quindi sostanzialmente Task.GetAwaiter().GetResult()equivale a await task. Presumo che la prima opzione sia utilizzata quando il metodo non può essere contrassegnato con async(costruttore per esempio). È corretto? Se sì, allora si scontra con la risposta migliore @ It'sNotALie
OlegI

5
@OlegI: Task.GetAwaiter().GetResult()è più equivalente a Task.Waite Task.Result(in quanto tutti e tre si bloccheranno in modo sincrono e avranno il potenziale per deadlock), ma Task.GetAwaiter().GetResult()ha il comportamento di propagazione dell'eccezione dell'attività waitit.
Nitin Agarwal,

Non è possibile evitare deadlock in questo scenario con (Task) .ConfigureAwait (false) .GetAwaiter (). GetResult (); ?
Daniel Lorenz

3
@DanielLorenz: vedi la seguente citazione: "Usare ConfigureAwait (false) per evitare deadlock è una pratica pericolosa. Dovresti usare ConfigureAwait (false) per ogni attesa nella chiusura transitiva di tutti i metodi chiamati dal codice di blocco, incluso il terzo - e codice di seconda parte. L'uso di ConfigureAwait (false) per evitare deadlock è nella migliore delle ipotesi solo un trucco. ... la soluzione migliore è "Non bloccare il codice asincrono". " - blog.stephencleary.com/2012/07/dont-block-on-async-code.html
Nitin Agarwal

4
Non capisco Task.Wait e Task.Result sono interrotti in base alla progettazione? Perché non sono resi obsoleti?
osexpert,

69

https://github.com/aspnet/Security/issues/59

"Un'ultima osservazione: dovresti evitare di usare Task.Resulte Task.Waitquanto più possibile incapsulano sempre l'eccezione interna in un AggregateExceptione sostituisci il messaggio con uno generico (si sono verificati uno o più errori), il che rende più difficile il debug. Anche se la versione sincrona non dovrebbe dovrebbe essere usato così spesso, dovresti prendere in seria considerazione di utilizzare Task.GetAwaiter().GetResult()invece ".


20
La fonte di riferimento qui è qualcuno che cita qualcun altro, senza riferimento. Considera il contesto: posso vedere molte persone ciecamente usare GetAwaiter (). GetResult () ovunque dopo aver letto questo.
Jack Ukleja,

2
Quindi non dovremmo usarlo?
tofutim,

11
Se due attività terminano con un'eccezione, perderai la seconda in questo scenario Task.WhenAll(task1, task2).GetAwaiter().GetResult();.
Mons.


33

Un'altra differenza è quando la asyncfunzione ritorna proprio Taskinvece di Task<T>allora non è possibile utilizzare

GetFooAsync(...).Result;

Mentre

GetFooAsync(...).GetAwaiter().GetResult();

funziona ancora.

So che il codice di esempio nella domanda è per il caso Task<T>, tuttavia la domanda viene posta in generale.


1
Questo non è vero. Dai
wojciech_rak

3
@wojciech_rak Nel codice, si utilizza Resultcon GetIntAsync()cui rendimenti Task<int>non solo Task. Ti consiglio di leggere di nuovo la mia risposta.
Nuri Tasdemir,

1
Hai ragione, all'inizio ho capito che rispondevi che non puoi GetFooAsync(...).Result all'interno di una funzione che ritorna Task. Questo ora ha senso, poiché non ci sono Proprietà vuote in C # ( Task.Resultè una proprietà), ma puoi ovviamente chiamare un metodo vuoto.
wojciech_rak,

22

Come già accennato se è possibile utilizzare await. Se hai bisogno di eseguire il codice in modo sincrono come dici tu .GetAwaiter().GetResult(), .Resulto .Wait()è un rischio per deadlock, come molti hanno detto nei commenti / risposte. Dal momento che molti di noi amano gli oneliner, puoi usarli per.Net 4.5<

Acquisizione di un valore tramite un metodo asincrono:

var result = Task.Run(() => asyncGetValue()).Result;

Chiamare in modo sincrono un metodo asincrono

Task.Run(() => asyncMethod()).Wait();

Non si verificheranno problemi di deadlock dovuti all'uso di Task.Run.

Fonte:

https://stackoverflow.com/a/32429753/3850405


1

Se un'attività fallisce, l'eccezione viene generata di nuovo quando il codice di continuazione chiama awaiter.GetResult (). Invece di chiamare GetResult, potremmo semplicemente accedere alla proprietà Result dell'attività. Il vantaggio di chiamare GetResult è che se l'attività fallisce, l'eccezione viene generata direttamente senza essere racchiusa in AggregateException, consentendo blocchi di cattura più semplici e più puliti.

Per attività non generiche, GetResult () ha un valore di ritorno vuoto. La sua utile funzione è quindi solo quella di riproporre le eccezioni.

fonte: c # 7.0 in breve

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.