Baru saja mendapat VS2012 dan mencoba menangani async
.
Katakanlah saya punya metode yang mengambil beberapa nilai dari sumber pemblokiran. Saya tidak ingin penelepon metode untuk memblokir. Saya bisa menulis metode untuk mengambil callback yang dipanggil ketika nilainya tiba, tetapi karena saya menggunakan C # 5, saya memutuskan untuk membuat metode async sehingga penelepon tidak harus berurusan dengan callback:
// contrived example (edited in response to Servy's comment)
public static Task<string> PromptForStringAsync(string prompt)
{
return Task.Factory.StartNew(() => {
Console.Write(prompt);
return Console.ReadLine();
});
}
Inilah contoh metode yang menyebutnya. Jika PromptForStringAsync
tidak async, metode ini akan membutuhkan peneleponan kembali di dalam panggilan balik. Dengan async, saya bisa menulis metode saya dengan cara yang sangat alami ini:
public static async Task GetNameAsync()
{
string firstname = await PromptForStringAsync("Enter your first name: ");
Console.WriteLine("Welcome {0}.", firstname);
string lastname = await PromptForStringAsync("Enter your last name: ");
Console.WriteLine("Name saved as '{0} {1}'.", firstname, lastname);
}
Sejauh ini baik. Masalahnya adalah ketika saya memanggil GetNameAsync:
public static void DoStuff()
{
GetNameAsync();
MainWorkOfApplicationIDontWantBlocked();
}
Intinya GetNameAsync
adalah asinkron. Saya tidak ingin memblokir, karena saya ingin kembali ke MainWorkOfApplicationIDontWantBlocked ASAP dan biarkan GetNameAsync melakukan hal itu di latar belakang. Namun, menyebutnya dengan cara ini memberi saya peringatan kompiler di GetNameAsync
telepon:
Warning 1 Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
Saya sangat menyadari bahwa "eksekusi metode saat ini berlanjut sebelum panggilan selesai". Itulah titik kode asynchronous, kan?
Saya lebih suka kode saya dikompilasi tanpa peringatan, tetapi tidak ada yang bisa "diperbaiki" di sini karena kode melakukan apa yang saya inginkan. Saya bisa menghilangkan peringatan dengan menyimpan nilai pengembalian GetNameAsync
:
public static void DoStuff()
{
var result = GetNameAsync(); // supress warning
MainWorkOfApplicationIDontWantBlocked();
}
Tapi sekarang saya punya kode berlebihan. Visual Studio tampaknya mengerti bahwa saya dipaksa untuk menulis kode yang tidak perlu ini, karena menekan peringatan "nilai tidak pernah digunakan" yang normal.
Saya juga bisa menghilangkan peringatan dengan membungkus GetNameAsync dengan metode yang tidak async:
public static Task GetNameWrapper()
{
return GetNameAsync();
}
Tapi itu kode yang lebih berlebihan. Jadi saya harus menulis kode saya tidak perlu atau mentolerir peringatan yang tidak perlu.
Apakah ada sesuatu tentang penggunaan async yang salah di sini?
sumber
PromptForStringAsync
Anda melakukan lebih banyak pekerjaan daripada yang Anda butuhkan; kembalikan saja hasilTask.Factory.StartNew
. Sudah tugas siapa yang nilainya adalah string yang dimasukkan di konsol. Tidak perlu menunggu untuk mengembalikan hasilnya; hal itu tidak menambah nilai baru.GetNameAsync
memberikan nama lengkap yang disediakan oleh pengguna (yaituTask<Name>
, daripada hanya mengembalikanTask
?DoStuff
Lalu dapat menyimpan tugas itu, dan apakahawait
itu setelah metode lain, atau bahkan meneruskan tugas ke yang lain metode sehingga bisaawait
atau diWait
suatu tempat di dalam implementasi ituasync
kata kunci.Jawaban:
Jika Anda benar-benar tidak membutuhkan hasilnya, Anda cukup mengubah
GetNameAsync
tanda tangan untuk mengembalikanvoid
:Pertimbangkan untuk melihat jawaban atas pertanyaan terkait: Apa perbedaan antara mengembalikan batal dan mengembalikan Tugas?
Memperbarui
Jika Anda membutuhkan hasilnya, Anda dapat mengubah
GetNameAsync
untuk kembali, katakanTask<string>
:Dan gunakan sebagai berikut:
sumber
GetNameAsync
tidak mengembalikan nilai apa pun (kecuali hasilnya sendiri, tentu saja).void
, dia tidak memiliki cara untuk mengetahui kapan itu dilakukan. Itulah yang saya maksud ketika saya mengatakan "hasil" dalam komentar saya sebelumnya.async void
metode kecuali untuk penangan acara.async void
pengecualian yang tidak Anda tangkap akan merusak proses Anda, tetapi dalam .net 4.5 itu akan tetap berjalan.Saya cukup terlambat untuk diskusi ini, tetapi ada juga opsi untuk menggunakan
#pragma
arahan pra-prosesor. Saya memiliki beberapa kode async di sana-sini yang secara eksplisit saya tidak ingin menunggu dalam beberapa kondisi, dan saya tidak menyukai peringatan dan variabel yang tidak digunakan sama seperti Anda semua:Itu
"4014"
datang dari halaman MSDN ini: Peringatan Kompiler (level 1) CS4014 .Lihat juga peringatan / jawaban oleh @ ryan-horath di sini https://stackoverflow.com/a/12145047/928483 .
Pembaruan untuk C # 7.0
C # 7.0 menambahkan fitur baru, membuang variabel: Buang - Panduan C # , yang juga dapat membantu dalam hal ini.
sumber
var
, cukup tulis_ = SomeMethodAsync();
Saya tidak terlalu menyukai solusi yang baik menetapkan tugas ke variabel yang tidak digunakan, atau mengubah metode tanda tangan untuk mengembalikan batal. Yang pertama membuat berlebihan, kode non-intuitif, sedangkan yang terakhir mungkin tidak dapat dilakukan jika Anda mengimplementasikan antarmuka atau menggunakan fungsi lain di mana Anda ingin menggunakan Tugas yang dikembalikan.
Solusi saya adalah membuat metode ekstensi dari Task, yang disebut DoNotAwait () yang tidak melakukan apa-apa. Ini tidak hanya akan menekan semua peringatan, ReSharper atau lainnya, tetapi membuat kode lebih mudah dipahami, dan menunjukkan kepada pengelola kode Anda di masa mendatang bahwa Anda benar-benar bermaksud agar panggilan tidak ditunggu.
Metode ekstensi:
Pemakaian:
Diedit untuk menambahkan: ini mirip dengan solusi Jonathan Allen di mana metode ekstensi akan memulai tugas jika belum dimulai, tetapi saya lebih suka memiliki fungsi tujuan tunggal sehingga maksud pemanggil benar-benar jelas.
sumber
async void
BURUK!Apa yang saya sarankan adalah Anda menjalankan secara eksplisit
Task
melalui metode anonim ...misalnya
Atau jika Anda memang ingin memblokirnya Anda bisa menunggu dengan metode anonim
Namun, jika
GetNameAsync
metode Anda harus berinteraksi dengan UI atau bahkan apa pun yang terikat UI, (WINRT / MVVM, saya melihat Anda), maka itu akan menjadi sedikit lebih lucu =)Anda harus meneruskan referensi ke dispatcher UI seperti ini ...
Dan kemudian dalam metode async Anda, Anda harus berinteraksi dengan UI atau elemen terikat UI berpikir bahwa pengirim ...
sumber
This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
Ini juga menyebabkan utas baru dibuat, sedangkan utas baru tidak harus dibuat dengan async / tunggu sendiri.Inilah yang sedang saya lakukan:
Di mana
RunConcurrently
didefinisikan sebagai ...https://github.com/docevaad/Anchor/blob/master/Tortuga.Anchor/Tortuga.Anchor.source/shared/TaskUtilities.cs
https://www.nuget.org/packages/Tortuga.Anchor/
sumber
public static void Forget(this Task task) { }
async Task
. Beberapa tugas harus dimulai secara manual.Menurut artikel Microsoft pada peringatan ini, Anda bisa menyelesaikannya dengan hanya menetapkan tugas yang dikembalikan ke variabel. Di bawah ini adalah terjemahan dari kode yang disediakan dalam contoh Microsoft:
Perhatikan bahwa melakukan ini akan menghasilkan pesan "Variabel lokal tidak pernah digunakan" di ReSharper.
sumber
Task
-mengembalikan fungsi harusawait
-ed kecuali Anda punya alasan yang sangat bagus untuk tidak melakukannya. Tidak ada alasan di sini mengapa membuang tugas akan lebih baik daripada jawaban yang sudah diterima menggunakanasync void
metode.async void
memperkenalkan masalah serius seputar penanganan kesalahan dan menghasilkan kode yang tidak dapat diuji (lihat artikel MSDN saya ). Akan jauh lebih baik menggunakan variabel - jika Anda benar - benar yakin Anda ingin pengecualian ditelan diam-diam. Lebih mungkin, op ingin memulai duaTask
dan kemudian melakukanawait Task.WhenAll
.async void DoNotWait(Task t) { await t; }
Metode penolong sederhana dapat digunakan untuk menghindari kelemahanasync void
metode yang Anda gambarkan. (Dan saya tidak berpikirTask.WhenAll
apa yang diinginkan OP, tapi bisa saja.)Di sini, solusi sederhana.
Salam
sumber
Ini contoh sederhana Anda yang menyebabkan kode superflous. Biasanya Anda ingin menggunakan data yang diambil dari sumber pemblokiran di beberapa titik dalam program, jadi Anda ingin hasilnya kembali sehingga mungkin untuk mendapatkan data.
Jika Anda benar-benar memiliki sesuatu yang terjadi sepenuhnya terisolasi dari sisa program, async tidak akan menjadi pendekatan yang tepat. Mulai saja utas baru untuk tugas itu.
sumber
async
dirancang untuk membersihkan ( misalnya )MethodWithCallback((result1) => { Use(result1); MethodWithCallback((result2) => { Use(result1,result2); })
Bahkan dalam contoh sepele ini, itu menyebalkan untuk diurai. Dengan async, kode yang setara dihasilkan untuk saya ketika saya menulisresult1 = await AsyncMethod(); Use(result1); result2 = await AsyncMethod(); Use(result1,result2);
Yang jauh lebih mudah dibaca (meskipun tidak ada yang sangat mudah dibaca dihancurkan bersama dalam komentar ini!)Use
.Apakah Anda benar-benar ingin mengabaikan hasilnya? seperti termasuk mengabaikan pengecualian yang tidak terduga?
Jika tidak, Anda mungkin ingin melihat pertanyaan ini: Api dan Lupakan pendekatan ,
sumber
Jika Anda tidak ingin mengubah tanda tangan metode untuk kembali
void
(karena pengembalianvoid
harus selalu dibatalkan ), Anda dapat menggunakan fitur C # 7.0+ Buang seperti ini, yang sedikit lebih baik daripada menetapkan ke variabel (dan harus menghapus sebagian besar lainnya) peringatan alat validasi sumber):sumber