Apakah Task.Result sama dengan .GetAwaiter.GetResult ()?

328

Saya baru-baru ini membaca beberapa kode yang menggunakan banyak metode async, tetapi kadang-kadang perlu menjalankannya secara sinkron. Kode tidak:

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

Apakah ini sama dengan

Foo foo = GetFooAsync(...).Result;
Jay Bazuzi
sumber
8
Dari dokumen GetResult: "Jenis ini dan anggotanya dimaksudkan untuk digunakan oleh kompiler." Orang lain seharusnya tidak menggunakannya.
pemboros
32
Ini disebut "sinkronisasi lebih dari async", dan kecuali Anda tahu bagaimana tugas tersebut diimplementasikan dapat menjadi ide yang sangat buruk. Ini dapat langsung menemui jalan buntu dalam banyak kasus (misalnya, async/ awaitmetode dalam MVC)
Marc Gravell
14
Di dunia nyata, kita memiliki konstruktor, kita memiliki antarmuka "tidak menunggu" yang perlu kita implementasikan, dan kita diberikan metode async di mana-mana. Saya akan senang menggunakan sesuatu yang hanya berfungsi tanpa saya harus bertanya-tanya mengapa itu "berbahaya", "tidak boleh digunakan" atau "hindari di semua biaya". Setiap saat saya harus mengacaukan async menjadi sakit kepala.
Larry

Jawaban:

173

Kurang lebih. Satu perbedaan kecil: jika Taskgagal, GetResult()hanya akan melempar pengecualian yang disebabkan secara langsung, sementara Task.Resultakan melempar AggregateException. Namun, apa gunanya menggunakan keduanya saat itu async? Opsi 100x lebih baik adalah menggunakan await.

Juga, Anda tidak seharusnya menggunakannya GetResult(). Ini dimaksudkan hanya untuk penggunaan kompiler, bukan untuk Anda. Tetapi jika Anda tidak ingin yang mengganggu AggregateException, gunakan itu.

Itu adalah Notalie.
sumber
27
@ JayBazuzi Tidak jika kerangka pengujian unit Anda mendukung tes unit async, yang saya pikir versi terbaru dari kebanyakan kerangka kerja lakukan.
svick
15
@ JayBazuzi: MSTest, xUnit, dan NUnit semua async Tasktes unit pendukung , dan miliki untuk beberapa waktu sekarang.
Stephen Cleary
18
menekan kembali pada 100x - itu 1000x lebih buruk untuk menggunakan menunggu jika Anda mengadaptasi kode lama dan menggunakan menunggu memerlukan penulisan ulang.
Terjebak
13
@AlexZhukovskiy: Saya tidak setuju .
Stephen Cleary
15
The 100x better option is to use await.Saya benci pernyataan seperti ini, jika saya bisa menampar awaitdi depannya saya akan. Tapi, ketika saya mencoba untuk mendapatkan kode async bekerja terhadap kode non-async seperti apa yang sering terjadi kepada saya banyak di Xamarin, saya akhirnya harus menggunakan hal-hal seperti ContinueWithbanyak dalam rangka untuk membuatnya tidak deadlock UI. Sunting: Saya tahu ini sudah tua, tetapi itu tidak mengurangi rasa frustrasi saya menemukan jawaban yang menyatakan ini tanpa ada alternatif untuk situasi di mana Anda tidak bisa hanya menggunakan await.
Thomas F.
147

Task.GetAwaiter().GetResult()lebih disukai daripada Task.Waitdan Task.Resultkarena menyebarkan pengecualian daripada membungkusnya dalam AggregateException. Namun, ketiga metode ini berpotensi menyebabkan masalah kebuntuan dan masalah pool thread. Mereka semua harus dihindari demi async/await.

Kutipan di bawah ini menjelaskan mengapa Task.Waitdan Task.Resulttidak hanya berisi perilaku propagasi pengecualian Task.GetAwaiter().GetResult()(karena "bilah kompatibilitas sangat tinggi").

Seperti yang saya sebutkan sebelumnya, kami memiliki bilah kompatibilitas yang sangat tinggi, dan karenanya kami menghindari perubahan. Karena itu, Task.Waitpertahankan perilaku awalnya yaitu selalu membungkus. Namun, Anda mungkin menemukan diri Anda dalam beberapa situasi lanjut di mana Anda ingin perilaku yang mirip dengan pemblokiran sinkron yang digunakan oleh Task.Wait, tetapi di mana Anda ingin pengecualian asli diperbanyak tanpa membuka dan tidak terbungkus dalam AggregateException. Untuk mencapai itu, Anda dapat menargetkan penunggu Tugas secara langsung. Ketika Anda menulis " await task;", kompiler menerjemahkannya menjadi penggunaan Task.GetAwaiter()metode, yang mengembalikan contoh yang memiliki GetResult()metode. Ketika digunakan pada Tugas yang salah, GetResult()akan menyebarkan pengecualian asli (ini adalah bagaimana " await task;" mendapatkan perilakunya). Dengan demikian Anda dapat menggunakan "task.GetAwaiter().GetResult()”Jika Anda ingin secara langsung mengaktifkan logika propagasi ini.

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

" GetResult" Sebenarnya berarti "memeriksa kesalahan tugas"

Secara umum, saya mencoba yang terbaik untuk menghindari pemblokiran secara sinkron pada tugas asinkron. Namun, ada beberapa situasi di mana saya melanggar pedoman itu. Dalam kondisi langka itu, metode pilihan saya adalah GetAwaiter().GetResult()karena mempertahankan pengecualian tugas daripada membungkusnya dalam AggregateException.

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

Nitin Agarwal
sumber
3
Jadi pada dasarnya Task.GetAwaiter().GetResult()setara dengan await task. Saya menganggap opsi pertama digunakan ketika metode tidak dapat ditandai dengan async(misalnya konstruktor). Apakah itu benar? Jika ya, maka itu bertabrakan dengan jawaban teratas @ It'sNotALie
OlegI
5
@OlegI: Task.GetAwaiter().GetResult()lebih setara dengan Task.Waitdan Task.Result(dalam hal ketiganya akan memblokir secara serempak dan memiliki potensi kebuntuan), tetapi Task.GetAwaiter().GetResult()memiliki perilaku propagasi pengecualian dari tugas yang menunggu.
Nitin Agarwal
Tidak bisakah Anda menghindari kebuntuan dalam skenario ini dengan (Tugas) .ConfigureAwait (false) .GetAwaiter (). GetResult (); ?
Daniel Lorenz
3
@DanielLorenz: Lihat kutipan berikut: "Menggunakan ConfigureAwait (false) untuk menghindari kebuntuan adalah praktik yang berbahaya. Anda harus menggunakan ConfigureAwait (false) untuk setiap menunggu dalam penutupan transitif semua metode yang disebut oleh kode pemblokiran, termasuk ketiga - dan kode pihak kedua. Menggunakan ConfigureAwait (false) untuk menghindari kebuntuan paling baik hanya peretasan) ... ... solusi yang lebih baik adalah "Jangan memblokir kode async". " - blog.stephencleary.com/2012/07/dont-block-on-async-code.html
Nitin Agarwal
4
Saya tidak mengerti. Task.Wait dan Task.Result rusak oleh desain? Mengapa mereka tidak menjadi usang?
osexpert
69

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

"Satu komentar terakhir: Anda harus menghindari penggunaan Task.Resultdan Task.Waitsebanyak mungkin karena mereka selalu merangkum pengecualian dalam dalam AggregateExceptiondan mengganti pesan dengan yang umum (Satu atau lebih kesalahan terjadi), yang membuat debugging lebih sulit. Bahkan jika versi sinkron seharusnya tidak sering digunakan, Anda harus mempertimbangkan untuk menggunakannya Task.GetAwaiter().GetResult(). "

scyuo
sumber
20
Sumber yang dirujuk di sini adalah seseorang yang mengutip orang lain, tanpa referensi. Pertimbangkan konteks: Saya dapat melihat banyak orang secara membabi buta menggunakan GetAwaiter (). GetResult () di mana-mana setelah membaca ini.
Jack Ukleja
2
Jadi kita seharusnya tidak menggunakannya?
tofutim
11
Jika dua tugas berakhir dengan pengecualian, Anda akan kehilangan yang kedua dalam skenario ini Task.WhenAll(task1, task2).GetAwaiter().GetResult();.
Monsinyur
Berikut contoh lain: github.com/aspnet/AspNetCore/issues/13611
George Chakhidze
33

Perbedaan lainnya adalah ketika asyncfungsi kembali hanya Taskdaripada Task<T>Anda tidak dapat menggunakan

GetFooAsync(...).Result;

Sedangkan

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

masih bekerja.

Saya tahu kode contoh dalam pertanyaan adalah untuk kasus ini Task<T>, namun pertanyaannya ditanyakan secara umum.

Nuri Tasdemir
sumber
1
Ini tidak benar. Lihatlah biola saya yang persis menggunakan konstruksi ini: dotnetfiddle.net/B4ewH8
wojciech_rak
3
@wojciech_rak Dalam kode Anda, Anda menggunakan Resultdengan GetIntAsync()yang kembali Task<int>tidak hanya Task. Saya sarankan Anda untuk membaca jawaban saya lagi.
Nuri Tasdemir
1
Anda benar, pada mulanya saya mengerti Anda menjawab bahwa Anda tidak bisa GetFooAsync(...).Result berada di dalam fungsi yang kembali Task. Ini sekarang masuk akal, karena tidak ada Properti kosong di C # ( Task.Resultadalah properti), tetapi Anda tentu saja dapat memanggil metode void.
wojciech_rak
22

Seperti yang sudah disebutkan jika bisa digunakan await. Jika Anda perlu menjalankan kode secara sinkron seperti yang Anda sebutkan .GetAwaiter().GetResult(), .Resultatau .Wait()berisiko untuk kebuntuan seperti yang banyak dikatakan dalam komentar / jawaban. Karena sebagian besar dari kita suka oneliners Anda dapat menggunakannya untuk.Net 4.5<

Memperoleh nilai melalui metode async:

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

Sinkron memanggil metode async

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

Tidak ada masalah jalan buntu yang akan terjadi karena penggunaan Task.Run.

Sumber:

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

Ogglas
sumber
1

Jika tugas salah, pengecualian dilemparkan kembali ketika kode lanjutan memanggil awaiter.GetResult (). Daripada memanggil GetResult, kita cukup mengakses properti Hasil dari tugas. Manfaat memanggil GetResult adalah jika tugasnya salah, pengecualian dilemparkan langsung tanpa dibungkus dengan AggregateException, memungkinkan blok tangkap yang lebih sederhana dan lebih bersih.

Untuk tugas non-generik, GetResult () memiliki nilai balik yang tidak valid. Fungsi yang berguna kemudian semata-mata untuk menyusun kembali pengecualian.

sumber: c # 7.0 secara singkat

Ali Abdollahi
sumber