Saya punya async
metode:
public async Task<string> GenerateCodeAsync()
{
string code = await GenerateCodeService.GenerateCodeAsync();
return code;
}
Saya perlu memanggil metode ini dari metode sinkron.
Bagaimana saya bisa melakukan ini tanpa harus menduplikasi GenerateCodeAsync
metode agar ini bekerja secara serempak?
Memperbarui
Namun tidak ada solusi masuk akal yang ditemukan.
Namun, saya melihat bahwa HttpClient
sudah menerapkan pola ini
using (HttpClient client = new HttpClient())
{
// async
HttpResponseMessage responseAsync = await client.GetAsync(url);
// sync
HttpResponseMessage responseSync = client.GetAsync(url).Result;
}
Jawaban:
Anda dapat mengakses
Result
properti tugas, yang akan menyebabkan utas Anda memblokir hingga hasilnya tersedia:Catatan: Dalam beberapa kasus, ini dapat menyebabkan kebuntuan: Panggilan Anda untuk
Result
memblokir utas utama, sehingga mencegah sisa kode async untuk dieksekusi. Anda memiliki opsi berikut untuk memastikan bahwa ini tidak terjadi:.ConfigureAwait(false)
ke metode perpustakaan Anda atausecara eksplisit jalankan metode async Anda di thread pool thread dan tunggu sampai selesai:
Ini tidak berarti bahwa Anda hanya perlu menambahkan tanpa sadar
.ConfigureAwait(false)
setelah semua panggilan async Anda! Untuk analisis terperinci tentang mengapa dan kapan Anda harus menggunakan.ConfigureAwait(false)
, lihat posting blog berikut:sumber
result
risiko kebuntuan, maka ketika adalah aman untuk mendapatkan hasilnya? Apakah setiap panggilan tidak sinkron memerlukanTask.Run
atauConfigureAwait(false)
?AspNetSynchronizationContext.Post
serialisasi kelanjutan async:Task newTask = _lastScheduledTask.ContinueWith(_ => SafeWrapCallback(action)); _lastScheduledTask = newTask;
Task.Run
agar tetap aman. Atau gunakan sesuatu sepertiWithNoContext
untuk mengurangi penggantian ulir yang berlebihan..Result
masih bisa menemui jalan buntu jika pemanggil berada di kumpulan utas itu sendiri. Ambil skenario di mana Thread Pool berukuran 32 dan 32 tugas sedang berjalan danWait()/Result
menunggu tugas ke-33 yang belum dijadwalkan yang ingin dijalankan di salah satu utas menunggu.Anda harus mendapatkan penunggu (
GetAwaiter()
) dan mengakhiri menunggu untuk penyelesaian tugas asinkron (GetResult()
).sumber
Task.GetAwaiter
: Metode ini dimaksudkan untuk penggunaan kompiler daripada untuk digunakan dalam kode aplikasi.Anda harus bisa menyelesaikan ini menggunakan delegasi, ekspresi lambda
sumber
Mungkin dengan
GenerateCodeAsync().Result
atauGenerateCodeAsync().Wait()
, seperti jawaban yang disarankan. Ini akan memblokir utas saat ini sampaiGenerateCodeAsync
selesai.Namun, pertanyaan Anda ditandai dengan asp.net, dan Anda juga meninggalkan komentar:
Maksud saya adalah, Anda seharusnya tidak memblokir metode asinkron dalam ASP.NET. Ini akan mengurangi skalabilitas aplikasi web Anda, dan dapat membuat jalan buntu (ketika
await
kelanjutan di dalamGenerateCodeAsync
diposting keAspNetSynchronizationContext
). MenggunakanTask.Run(...).Result
untuk melepas sesuatu ke thread pool dan kemudian memblokir skalabilitas akan semakin menyakitkan, karena ia membuat +1 lebih banyak thread untuk memproses permintaan HTTP yang diberikan.ASP.NET memiliki dukungan bawaan untuk metode asinkron, baik melalui pengontrol asinkron (dalam ASP.NET MVC dan Web API) atau langsung melalui
AsyncManager
danPageAsyncTask
dalam ASP.NET klasik. Anda harus menggunakannya. Untuk lebih jelasnya, periksa jawaban ini .sumber
SaveChanges()
metodeDbContext
, dan di sini saya memanggil metode async, jadi sayangnya kontroler async tidak akan membantu saya dalam situasi iniSaveChangesAsync
danSaveChanges
, pastikan mereka tidak dipanggil keduanya dalam proyek ASP.NET yang sama..NET MVC
filter mendukung kode asinkronIAuthorizationFilter
, jadi saya tidak bisa menggunakanasync
semuanyaMicrosoft Identity memiliki metode ekstensi yang memanggil metode async secara sinkron. Misalnya ada metode GenerateUserIdentityAsync () dan CreateIdentity () yang sama
Jika Anda melihat UserManagerExtensions.CreateIdentity () terlihat seperti ini:
Sekarang mari kita lihat apa yang dilakukan AsyncHelper.RunSync
Jadi, ini adalah pembungkus Anda untuk metode async. Dan tolong jangan membaca data dari Hasil - ini berpotensi memblokir kode Anda di ASP.
Ada cara lain - yang mencurigakan bagi saya, tetapi Anda dapat mempertimbangkannya juga
sumber
Untuk mencegah kebuntuan, saya selalu mencoba menggunakan
Task.Run()
ketika saya harus memanggil metode async secara serentak yang @Heinzi sebutkan.Namun metode tersebut harus dimodifikasi jika metode async menggunakan parameter. Misalnya
Task.Run(GenerateCodeAsync("test")).Result
memberi kesalahan:Ini bisa disebut seperti ini sebagai gantinya:
sumber
Sebagian besar jawaban di utas ini rumit atau akan mengakibatkan kebuntuan.
Metode berikut ini sederhana dan akan menghindari kebuntuan karena kami menunggu tugas selesai dan baru kemudian mendapatkan hasilnya
Selanjutnya, berikut ini adalah referensi ke artikel MSDN yang berbicara tentang hal yang persis sama- https://blogs.msdn.microsoft.com/jpsanders/2017/08/28/asp-net-do-not-use-use-task-result- dalam konteks utama /
sumber
Saya lebih suka pendekatan yang tidak menghalangi:
sumber
Saya menggunakan pendekatan ini:
sumber
Cara lain bisa jadi jika Anda ingin menunggu sampai tugas selesai:
sumber
EDIT:
Tugas memiliki metode Tunggu, Task.Wait (), yang menunggu "janji" untuk menyelesaikan dan kemudian melanjutkan, sehingga menjadikannya sinkron. contoh:
sumber
Jika Anda memiliki metode async yang disebut " RefreshList ", Anda dapat memanggil metode async dari metode non-async seperti di bawah ini.
sumber