Saya memiliki antarmuka yang mengekspos beberapa metode asinkron. Lebih khusus lagi, ia memiliki metode yang ditentukan yang mengembalikan Tugas atau Tugas <T>. Saya menggunakan kata kunci async / await.
Saya sedang dalam proses menerapkan antarmuka ini. Namun, dalam beberapa metode ini, implementasi ini tidak memiliki apa-apa untuk menunggu. Untuk alasan itu saya mendapatkan peringatan compiler "Metode async ini tidak memiliki operator 'await' dan akan berjalan serentak ..."
Saya mengerti mengapa saya mendapatkan kesalahan tetapi saya bertanya-tanya apakah saya harus melakukan sesuatu tentang mereka dalam konteks ini. Rasanya salah jika mengabaikan peringatan compiler.
Saya tahu saya bisa memperbaikinya dengan menunggu di Task.Run tetapi itu terasa salah untuk metode yang hanya melakukan beberapa operasi murah. Ini juga terdengar seperti itu akan menambah overhead yang tidak diperlukan ke eksekusi tetapi kemudian saya juga tidak yakin apakah itu sudah ada karena kata kunci async ada.
Haruskah saya mengabaikan peringatan atau adakah cara untuk mengatasi hal ini yang tidak saya lihat?
sumber
async
?async
kata kunci tersebut. Anda masih bisa kembaliTask
menggunakanTask.FromResult
.Task.FromResult
.Jawaban:
Kata kunci async hanyalah sebuah detail implementasi dari sebuah metode; itu bukan bagian dari tanda tangan metode. Jika salah satu implementasi atau penggantian metode tertentu tidak memiliki apa-apa untuk menunggu, hilangkan saja kata kunci async dan kembalikan tugas yang sudah selesai menggunakan Task.FromResult <TResult> :
public Task<string> Foo() // public async Task<string> Foo() { // { Baz(); // Baz(); return Task.FromResult("Hello"); // return "Hello"; } // }
Jika metode Anda mengembalikan Tugas dan bukan Tugas <TResult> , Anda dapat mengembalikan tugas yang sudah selesai dari semua jenis dan nilai.
Task.FromResult(0)
tampaknya menjadi pilihan populer:public Task Bar() // public async Task Bar() { // { Baz(); // Baz(); return Task.FromResult(0); // } // }
Atau, mulai .NET Framework 4.6, Anda dapat mengembalikan Task.CompletedTask :
public Task Bar() // public async Task Bar() { // { Baz(); // Baz(); return Task.CompletedTask; // } // }
sumber
await Task.FromResult(0)
? Bagaimana denganawait Task.Yield()
?async
metode non- , Anda kembaliTask.FromResult(0)
alih-alih menunggu.Sangat masuk akal bahwa beberapa operasi "asinkron" selesai secara serempak, namun tetap sesuai dengan model panggilan asinkron demi polimorfisme.
Contoh dunia nyata ini adalah dengan OS I / O API. Panggilan asinkron dan tumpang tindih pada beberapa perangkat selalu selesai sebaris (menulis ke pipa yang diimplementasikan menggunakan memori bersama, misalnya). Tapi mereka mengimplementasikan antarmuka yang sama seperti operasi multi-bagian yang berlanjut di latar belakang.
sumber
Michael Liu menjawab dengan baik pertanyaan Anda tentang bagaimana Anda dapat menghindari peringatan: dengan mengembalikan Task.FromResult.
Saya akan menjawab bagian "Haruskah saya khawatir tentang peringatan" dari pertanyaan Anda.
Jawabannya iya!
Alasannya adalah karena peringatan sering muncul saat Anda memanggil metode yang mengembalikan
Task
di dalam metode asinkron tanpaawait
operator. Saya baru saja memperbaiki bug konkurensi yang terjadi karena saya menjalankan operasi di Entity Framework tanpa menunggu operasi sebelumnya.Jika Anda dapat dengan cermat menulis kode Anda untuk menghindari peringatan compiler, maka ketika ada peringatan, itu akan menonjol seperti jempol yang sakit. Saya bisa menghindari beberapa jam debugging.
sumber
await
di dalam metode di satu tempat (tidak akan ada CS1998) tetapi itu tidak berarti bahwa tidak akan ada panggilan lain dari metode asnyc yang tidak memiliki sinkronisasi (menggunakanawait
atau lainnya). Sekarang jika seseorang ingin tahu cara memastikan Anda tidak melewatkan sinkronisasi secara tidak sengaja, pastikan Anda tidak mengabaikan peringatan lain - CS4014. Saya bahkan akan merekomendasikan untuk mengancam yang satu itu sebagai kesalahan.Mungkin sudah terlambat tapi mungkin investigasi berguna:
Ada tentang struktur dalam kode terkompilasi ( IL ):
public static async Task<int> GetTestData() { return 12; }
menjadi di IL:
.method private hidebysig static class [mscorlib]System.Threading.Tasks.Task`1<int32> GetTestData() cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.AsyncStateMachineAttribute::.ctor(class [mscorlib]System.Type) = ( 01 00 28 55 73 61 67 65 4C 69 62 72 61 72 79 2E // ..(UsageLibrary. 53 74 61 72 74 54 79 70 65 2B 3C 47 65 74 54 65 // StartType+<GetTe 73 74 44 61 74 61 3E 64 5F 5F 31 00 00 ) // stData>d__1.. .custom instance void [mscorlib]System.Diagnostics.DebuggerStepThroughAttribute::.ctor() = ( 01 00 00 00 ) // Code size 52 (0x34) .maxstack 2 .locals init ([0] class UsageLibrary.StartType/'<GetTestData>d__1' V_0, [1] valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> V_1) IL_0000: newobj instance void UsageLibrary.StartType/'<GetTestData>d__1'::.ctor() IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: call valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<!0> valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::Create() IL_000c: stfld valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> UsageLibrary.StartType/'<GetTestData>d__1'::'<>t__builder' IL_0011: ldloc.0 IL_0012: ldc.i4.m1 IL_0013: stfld int32 UsageLibrary.StartType/'<GetTestData>d__1'::'<>1__state' IL_0018: ldloc.0 IL_0019: ldfld valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> UsageLibrary.StartType/'<GetTestData>d__1'::'<>t__builder' IL_001e: stloc.1 IL_001f: ldloca.s V_1 IL_0021: ldloca.s V_0 IL_0023: call instance void valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::Start<class UsageLibrary.StartType/'<GetTestData>d__1'>(!!0&) IL_0028: ldloc.0 IL_0029: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32> UsageLibrary.StartType/'<GetTestData>d__1'::'<>t__builder' IL_002e: call instance class [mscorlib]System.Threading.Tasks.Task`1<!0> valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int32>::get_Task() IL_0033: ret } // end of method StartType::GetTestData
Dan tanpa async dan metode tugas:
public static int GetTestData() { return 12; }
menjadi :
.method private hidebysig static int32 GetTestData() cil managed { // Code size 8 (0x8) .maxstack 1 .locals init ([0] int32 V_0) IL_0000: nop IL_0001: ldc.i4.s 12 IL_0003: stloc.0 IL_0004: br.s IL_0006 IL_0006: ldloc.0 IL_0007: ret } // end of method StartType::GetTestData
Seperti yang Anda lihat perbedaan besar antara metode ini. Jika Anda tidak menggunakan metode await inside async dan tidak peduli tentang penggunaan metode async (misalnya panggilan API atau event handler), sebaiknya Anda mengubahnya menjadi metode sinkronisasi normal (ini menghemat kinerja aplikasi Anda).
Diperbarui:
Ada juga informasi tambahan dari microsoft docs https://docs.microsoft.com/en-us/dotnet/standard/async-in-depth :
sumber
async/await
sangat disederhanakan karena Anda mendasarkannya pada contoh tidak realistis dari operasi tunggal yang terikat CPU.Task
Jika digunakan dengan benar memungkinkan peningkatan kinerja dan daya tanggap aplikasi karena tugas bersamaan (yaitu paralel) dan pengelolaan serta penggunaan utas yang lebih baikTasks
. Ini adalah cerita sedih bahwa Anda tidak membaca seluruh teks dari posting dan membuat kesimpulan dengan cepat.int
(seperti dalam kasus Anda) dan yang mengembalikanTask
seperti yang dibahas oleh OP. Baca nya pos dan jawaban diterima lagi bukannya mengambil hal-hal pribadi. Jawaban Anda tidak membantu dalam kasus ini. Anda bahkan tidak perlu repot-repot untuk menunjukkan perbedaan antara metode yang adaawait
di dalam atau tidak. Sekarang seandainya Anda melakukannya, itu akan sangat bagus untuk mendapat upvoteCatatan tentang perilaku pengecualian saat kembali
Task.FromResult
Berikut adalah demo kecil yang menunjukkan perbedaan dalam penanganan pengecualian antara metode yang ditandai dan tidak ditandai
async
.public Task<string> GetToken1WithoutAsync() => throw new Exception("Ex1!"); // Warning: This async method lacks 'await' operators and will run synchronously. Consider ... public async Task<string> GetToken2WithAsync() => throw new Exception("Ex2!"); public string GetToken3Throws() => throw new Exception("Ex3!"); public async Task<string> GetToken3WithAsync() => await Task.Run(GetToken3Throws); public async Task<string> GetToken4WithAsync() { throw new Exception("Ex4!"); return await Task.FromResult("X");} public static async Task Main(string[] args) { var p = new Program(); try { var task1 = p.GetToken1WithoutAsync(); } catch( Exception ) { Console.WriteLine("Throws before await.");}; var task2 = p.GetToken2WithAsync(); // Does not throw; try { var token2 = await task2; } catch( Exception ) { Console.WriteLine("Throws on await.");}; var task3 = p.GetToken3WithAsync(); // Does not throw; try { var token3 = await task3; } catch( Exception ) { Console.WriteLine("Throws on await.");}; var task4 = p.GetToken4WithAsync(); // Does not throw; try { var token4 = await task4; } catch( Exception ) { Console.WriteLine("Throws on await.");}; }
// .NETCoreApp,Version=v3.0 Throws before await. Throws on await. Throws on await. Throws on await.
(Posting silang dari jawaban saya untuk When async Task <T> diperlukan oleh antarmuka, bagaimana mendapatkan variabel kembali tanpa peringatan kompiler )
sumber