Saya sedang mengerjakan proyek yang perlu mendukung versi async dan sinkronisasi dari logika / metode yang sama. Jadi misalnya saya harus punya:
public class Foo
{
public bool IsIt()
{
using (var conn = new SqlConnection(DB.ConnString))
{
return conn.Query<bool>("SELECT IsIt FROM SomeTable");
}
}
public async Task<bool> IsItAsync()
{
using (var conn = new SqlConnection(DB.ConnString))
{
return await conn.QueryAsync<bool>("SELECT IsIt FROM SomeTable");
}
}
}
Logika async dan sinkronisasi untuk metode ini identik dalam segala hal kecuali bahwa yang satu adalah async dan yang lain tidak. Apakah ada cara yang sah untuk menghindari pelanggaran prinsip KERING dalam skenario semacam ini? Saya melihat bahwa orang mengatakan Anda bisa menggunakan GetAwaiter (). GetResult () pada metode async dan memintanya dari metode sinkronisasi Anda? Apakah utas itu aman di semua skenario? Apakah ada cara lain yang lebih baik untuk melakukan ini atau saya terpaksa menduplikasi logikanya?
c#
.net
asynchronous
Marko
sumber
sumber
return Task.FromResult(IsIt());
)Jawaban:
Anda mengajukan beberapa pertanyaan dalam pertanyaan Anda. Saya akan memecahnya sedikit berbeda dari yang Anda lakukan. Tapi pertama izinkan saya langsung menjawab pertanyaan.
Kita semua menginginkan kamera yang ringan, berkualitas tinggi, dan murah, tetapi seperti kata pepatah, Anda hanya bisa mendapatkan paling banyak dua dari tiga. Anda berada dalam situasi yang sama di sini. Anda menginginkan solusi yang efisien, aman, dan berbagi kode antara jalur sinkron dan asinkron. Anda hanya akan mendapatkan dua dari itu.
Biarkan saya menjelaskan mengapa itu. Kami akan mulai dengan pertanyaan ini:
Inti dari pertanyaan ini adalah "dapatkah saya membagikan jalur sinkron dan asinkron dengan membuat jalur sinkron hanya menunggu secara sinkron pada versi asinkron?"
Biarkan saya menjadi sangat jelas tentang hal ini karena ini penting:
ANDA HARUS SEGERA BERHENTI MENGAMBIL NASIHAT DARI MEREKA ORANG .
Itu saran yang sangat buruk. Sangat berbahaya untuk secara sinkron mengambil hasil dari tugas asinkron kecuali Anda memiliki bukti bahwa tugas telah selesai secara normal atau tidak normal .
Alasan mengapa ini saran yang sangat buruk adalah, yah, pertimbangkan skenario ini. Anda ingin memotong rumput, tetapi bilah pemotong rumput Anda rusak. Anda memutuskan untuk mengikuti alur kerja ini:
Apa yang terjadi? Anda tidur selamanya karena operasi memeriksa surat sekarang terjaga keamanannya pada sesuatu yang terjadi setelah surat tiba .
Sangat mudah untuk masuk ke situasi ini ketika Anda secara serentak menunggu tugas yang sewenang-wenang. Tugas itu mungkin sudah dijadwalkan di masa depan utas yang sekarang menunggu , dan sekarang masa depan itu tidak akan pernah tiba karena Anda sedang menunggu untuk itu.
Jika Anda melakukan menunggu asinkron maka semuanya baik-baik saja! Anda memeriksa surat secara berkala, dan saat Anda menunggu, Anda membuat sandwich atau melakukan pajak atau apa pun; Anda terus menyelesaikan pekerjaan sambil menunggu.
Jangan menunggu secara sinkron. Jika tugas selesai, itu tidak perlu . Jika tugas tidak dilakukan tetapi dijadwalkan untuk menjalankan utas saat ini, itu tidak efisien karena utas saat ini dapat melayani pekerjaan lain alih-alih menunggu. Jika tugas tidak selesai dan jadwal berjalan pada utas saat ini, itu tergantung menunggu secara sinkron. Tidak ada alasan yang baik untuk menunggu secara sinkron, lagi, kecuali jika Anda sudah tahu bahwa tugas sudah selesai .
Untuk membaca lebih lanjut tentang topik ini, lihat
https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
Stephen menjelaskan skenario dunia nyata jauh lebih baik daripada yang saya bisa.
Sekarang mari kita pertimbangkan "arah lain". Bisakah kita membagikan kode dengan membuat versi asinkron hanya melakukan versi sinkron pada utas pekerja?
Itu mungkin dan memang mungkin ide yang buruk, karena alasan berikut.
Itu tidak efisien jika operasi sinkron adalah pekerjaan IO latensi tinggi. Ini pada dasarnya mempekerjakan seorang pekerja dan membuat pekerja itu tidur sampai tugas selesai. Utas sangat mahal . Mereka mengkonsumsi sejuta byte address space minimum secara default, mereka butuh waktu, mereka mengambil sumber daya sistem operasi; Anda tidak ingin membakar utas melakukan pekerjaan yang tidak berguna.
Operasi sinkron mungkin tidak ditulis agar aman utas.
Ini adalah teknik yang lebih masuk akal jika pekerjaan dengan latensi tinggi terikat prosesor, tetapi jika demikian maka Anda mungkin tidak ingin menyerahkannya kepada pekerja. Anda mungkin ingin menggunakan pustaka tugas paralel untuk memparalelkannya ke CPU sebanyak mungkin, Anda mungkin ingin logika pembatalan, dan Anda tidak bisa begitu saja membuat versi sinkron melakukan semua itu, karena dengan demikian itu akan menjadi versi asinkron .
Bacaan lebih lanjut; sekali lagi, Stephen menjelaskannya dengan sangat jelas:
Mengapa tidak menggunakan Task.Run:
https://blog.stephencleary.com/2013/11/taskrun-etiquette-examples-using.html
Lebih banyak skenario "lakukan dan jangan" untuk Task.Run:
https://blog.stephencleary.com/2013/11/taskrun-etiquette-examples-dont-use.html
Lalu, apa yang tersisa dari kita? Kedua teknik untuk berbagi kode menyebabkan deadlock atau inefisiensi besar. Kesimpulan yang kami capai adalah bahwa Anda harus membuat pilihan. Apakah Anda ingin program yang efisien dan benar serta menyenangkan penelepon, atau Anda ingin menyimpan beberapa penekanan tombol yang diperlukan dengan menduplikasi sejumlah kecil kode antara jalur sinkron dan asinkron? Anda tidak mendapatkan keduanya, saya khawatir.
sumber
Task.Run(() => SynchronousMethod())
dalam versi async bukan yang diinginkan OP?Dns.GetHostEntryAsync
diterapkan di .NET, danFileStream.ReadAsync
untuk jenis file tertentu. OS tidak menyediakan antarmuka async, jadi runtime harus memalsukannya (dan itu bukan bahasa yang spesifik — katakanlah, runtime Erlang menjalankan seluruh pohon proses pekerja , dengan beberapa utas di dalamnya, untuk menyediakan disk I / O dan nama non-blocking resolusi).Sulit untuk memberikan jawaban satu ukuran untuk semua untuk yang satu ini. Sayangnya tidak ada cara sederhana dan sempurna untuk menggunakan kembali antara kode asinkron dan sinkron. Tetapi di sini ada beberapa prinsip untuk dipertimbangkan:
Query()
satu danQueryAsync()
yang lain), atau mengatur koneksi dengan pengaturan yang berbeda. Jadi, walaupun secara struktural mirip, sering kali ada cukup banyak perbedaan perilaku sehingga pantas diperlakukan sebagai kode terpisah dengan persyaratan berbeda. Perhatikan perbedaan antara implementasi metode Async dan Sinkronisasi di kelas File, misalnya: tidak ada upaya yang dilakukan untuk membuatnya menggunakan kode yang samaTask.FromResult(...)
.Semoga berhasil.
sumber
Mudah; memiliki panggilan sinkron satu async. Bahkan ada metode praktis
Task<T>
untuk melakukan hal itu:sumber