Saya belajar tentang async / menunggu, dan mengalami situasi di mana saya perlu memanggil metode async secara serempak. Bagaimana saya bisa melakukan itu?
Metode Async:
public async Task<Customers> GetCustomers()
{
return await Service.GetCustomersAsync();
}
Penggunaan normal:
public async void GetCustomers()
{
customerList = await GetCustomers();
}
Saya sudah mencoba menggunakan yang berikut ini:
Task<Customer> task = GetCustomers();
task.Wait()
Task<Customer> task = GetCustomers();
task.RunSynchronously();
Task<Customer> task = GetCustomers();
while(task.Status != TaskStatus.RanToCompletion)
Saya juga mencoba saran dari sini , tetapi tidak berfungsi ketika operator dalam keadaan ditangguhkan.
public static void WaitWithPumping(this Task task)
{
if (task == null) throw new ArgumentNullException(“task”);
var nestedFrame = new DispatcherFrame();
task.ContinueWith(_ => nestedFrame.Continue = false);
Dispatcher.PushFrame(nestedFrame);
task.Wait();
}
Berikut adalah pengecualian dan susun jejak dari panggilan RunSynchronously
:
System.InvalidOperationException
Pesan : RunSynchronously tidak dapat dipanggil pada tugas yang tidak terikat ke delegasi.
InnerException : null
Sumber : mscorlib
StackTrace :
at System.Threading.Tasks.Task.InternalRunSynchronously(TaskScheduler scheduler)
at System.Threading.Tasks.Task.RunSynchronously()
at MyApplication.CustomControls.Controls.MyCustomControl.CreateAvailablePanelList() in C:\Documents and Settings\...\MyApplication.CustomControls\Controls\MyCustomControl.xaml.cs:line 638
at MyApplication.CustomControls.Controls.MyCustomControl.get_AvailablePanels() in C:\Documents and Settings\...\MyApplication.CustomControls\Controls\MyCustomControl.xaml.cs:line 233
at MyApplication.CustomControls.Controls.MyCustomControl.<CreateOpenPanelList>b__36(DesktopPanel panel) in C:\Documents and Settings\...\MyApplication.CustomControls\Controls\MyCustomControl.xaml.cs:line 597
at System.Collections.Generic.List`1.ForEach(Action`1 action)
at MyApplication.CustomControls.Controls.MyCustomControl.<CreateOpenPanelList>d__3b.MoveNext() in C:\Documents and Settings\...\MyApplication.CustomControls\Controls\MyCustomControl.xaml.cs:line 625
at System.Runtime.CompilerServices.TaskAwaiter.<>c__DisplayClass7.<TrySetContinuationForAwait>b__1(Object state)
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
at System.Windows.Threading.DispatcherOperation.InvokeImpl()
at System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state)
at System.Threading.ExecutionContext.runTryCode(Object userData)
at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Windows.Threading.DispatcherOperation.Invoke()
at System.Windows.Threading.Dispatcher.ProcessQueue()
at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
at System.Windows.Threading.Dispatcher.InvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
at System.Windows.Threading.Dispatcher.Run()
at System.Windows.Application.RunDispatcher(Object ignore)
at System.Windows.Application.RunInternal(Window window)
at System.Windows.Application.Run(Window window)
at System.Windows.Application.Run()
at MyApplication.App.Main() in C:\Documents and Settings\...\MyApplication\obj\Debug\App.g.cs:line 50
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
c#
asynchronous
c#-5.0
async-await
Rachel
sumber
sumber
Task
serempak; 3) Saya mengintegrasikan GeneXus dengan driver MongoDB C # , yang mengekspos beberapa metode hanya secara tidak sinkronSemaphoreSlim
.Jawaban:
Berikut adalah solusi yang saya temukan yang berfungsi untuk semua kasus (termasuk dispatcher yang ditangguhkan). Ini bukan kode saya dan saya masih bekerja untuk sepenuhnya memahaminya, tetapi itu berhasil.
Itu bisa disebut menggunakan:
customerList = AsyncHelpers.RunSync<List<Customer>>(() => GetCustomers());
Kode berasal dari sini
sumber
DynamicNodeProviderBase
kelas dasar, seseorang tidak dapat mendeklarasikannya sebagaiasync
metode. Entah saya harus mengganti dengan perpustakaan baru, atau hanya memanggil op sinkron.AspNetSynchronizationContext
, jadi peretasan khusus ini tidak akan berfungsi jika Anda memanggil API tersebut.Maklum jawaban ini berumur tiga tahun. Saya menulisnya sebagian besar didasarkan pada pengalaman dengan. Net 4.0, dan sangat sedikit dengan 4,5 terutama dengan
async-await
. Secara umum itu adalah solusi sederhana yang bagus, tetapi kadang-kadang merusak banyak hal. Silakan baca diskusi di komentar..Net 4.5
Gunakan ini saja:
Lihat: TaskAwaiter , Task.Result , Task.RunSynchronously
.Net 4.0
Gunakan ini:
...atau ini:
sumber
.Result
dapat menghasilkan jalan buntu dalam skenario tertentuResult
dapat dengan mudah menyebabkan kebuntuan dalamasync
kode , seperti yang saya jelaskan di blog saya.Terkejut tidak ada yang menyebutkan ini:
Tidak secantik beberapa metode lain di sini, tetapi memiliki manfaat sebagai berikut:
Wait
)AggregateException
(sepertiResult
)Task
danTask<T>
( coba sendiri! )Juga, karena
GetAwaiter
diketik bebek, ini harus bekerja untuk objek apa pun yang dikembalikan dari metode async (sukaConfiguredAwaitable
atauYieldAwaitable
), bukan hanya Tugas.sunting: Harap perhatikan bahwa pendekatan ini (atau menggunakan
.Result
) mungkin menemui jalan buntu, kecuali jika Anda memastikan untuk menambahkan.ConfigureAwait(false)
setiap kali Anda menunggu, untuk semua metode async yang mungkin dapat dicapai dariBlahAsync()
(bukan hanya yang dipanggil langsung). Penjelasan .Jika Anda terlalu malas untuk menambahkan di
.ConfigureAwait(false)
mana-mana, dan Anda tidak peduli dengan kinerja, Anda juga dapat melakukannyasumber
GetAwaiter()
, "Metode ini ditujukan untuk pengguna kompiler daripada digunakan langsung dalam kode."Jauh lebih mudah untuk menjalankan tugas di kumpulan utas, daripada mencoba menipu penjadwal untuk menjalankannya secara serempak. Dengan begitu Anda dapat yakin bahwa itu tidak akan menemui jalan buntu. Kinerja terpengaruh karena saklar konteks.
sumber
Task.Run(DoSomethingAsync)
saja? Ini menghapus satu tingkat delegasi.Task<MyResult> task = Task.Run(async () => await DoSomethingAsync());
lebih eksplisit dan mengatasi kekhawatiran oleh @sgnsajgon bahwa mungkin akan mengembalikan Tugas <Tugas <MyResult>>. Overload Task.Run yang benar dipilih dengan cara apa pun, tetapi delegasi async menjadikan maksud Anda jelas.Jawaban terbaik adalah Anda tidak , dengan detail tergantung pada apa "situasi" itu.
Apakah ini pengambil properti? Dalam kebanyakan kasus, lebih baik memiliki metode asinkron daripada "properti asinkron". (Untuk info lebih lanjut, lihat posting blog saya di properti asinkron ).
Apakah ini aplikasi MVVM dan Anda ingin melakukan pengikatan data asinkron? Kemudian gunakan sesuatu seperti saya
NotifyTask
, seperti yang dijelaskan dalam artikel MSDN saya tentang pengikatan data asinkron .Apakah itu konstruktor? Maka Anda mungkin ingin mempertimbangkan metode pabrik asinkron. (Untuk info lebih lanjut, lihat posting blog saya di konstruktor asinkron ).
Hampir selalu ada jawaban yang lebih baik daripada melakukan sync-over-async.
Jika tidak mungkin untuk situasi Anda (dan Anda tahu ini dengan mengajukan pertanyaan di sini menggambarkan situasi ), maka saya akan merekomendasikan hanya menggunakan kode sinkron. Async adalah yang terbaik; sinkronisasi semua cara adalah yang terbaik kedua. Sinkronisasi-over-async tidak dianjurkan.
Namun, ada beberapa situasi di mana sinkronisasi-over-async diperlukan. Khususnya, Anda dibatasi oleh kode panggilan sehingga Anda harus disinkronkan (dan sama sekali tidak memiliki cara untuk memikirkan kembali atau menyusun kembali kode Anda untuk memungkinkan sinkronisasi), dan Anda harus memanggil kode async. Ini adalah situasi yang sangat langka, tetapi muncul dari waktu ke waktu.
Dalam hal ini, Anda harus menggunakan salah satu peretasan yang dijelaskan dalam artikel saya tentang pengembangan brownfield
async
, khususnya:GetAwaiter().GetResult()
). Perhatikan bahwa ini dapat menyebabkan kebuntuan (seperti yang saya jelaskan di blog saya).Task.Run(..).GetAwaiter().GetResult()
). Perhatikan bahwa ini hanya akan berfungsi jika kode asinkron dapat dijalankan pada utas kumpulan utas (yaitu, tidak bergantung pada konteks UI atau ASP.NET).Loop pesan bersarang adalah yang paling berbahaya dari semua peretasan, karena hal itu menyebabkan pemanggilan kembali . Re-entrancy sangat sulit untuk dipikirkan, dan (IMO) adalah penyebab sebagian besar bug aplikasi pada Windows. Secara khusus, jika Anda berada di utas UI dan Anda memblokir antrian pekerjaan (menunggu pekerjaan async selesai), maka CLR benar-benar melakukan beberapa pemompaan pesan untuk Anda - itu sebenarnya akan menangani beberapa pesan Win32 dari dalam Anda kode . Oh, dan Anda tidak tahu pesan mana - ketika Chris Brumme berkata, "Bukankah lebih baik mengetahui dengan pasti apa yang akan dipompa? Sayangnya, memompa adalah seni hitam yang berada di luar jangkauan pemahaman fana." , maka kita benar-benar tidak memiliki harapan untuk mengetahui.
Jadi, ketika Anda memblokir seperti ini di utas UI, Anda meminta masalah. Kutipan cbrumme lain dari artikel yang sama: "Dari waktu ke waktu, pelanggan di dalam atau di luar perusahaan menemukan bahwa kami memompa pesan selama pemblokiran berhasil pada STA [utas UI]. Ini adalah kekhawatiran yang sah, karena mereka tahu bahwa itu sangat sulit untuk menulis kode yang kuat dalam menghadapi reentrancy. "
Ya itu. Sangat sulit untuk menulis kode yang kuat dalam menghadapi reentrancy. Dan loop pesan bersarang memaksa Anda untuk menulis kode yang kuat dalam menghadapi reentrancy. Inilah sebabnya mengapa jawaban yang diterima (dan yang paling banyak dipilih) untuk pertanyaan ini sangat berbahaya dalam praktiknya.
Jika Anda benar-benar keluar dari semua opsi lain - Anda tidak dapat mendesain ulang kode Anda, Anda tidak dapat menata ulang menjadi async - Anda dipaksa oleh kode panggilan yang tidak dapat diubah untuk disinkronkan - Anda tidak dapat mengubah kode hilir menjadi sinkronisasi - Anda tidak dapat memblokir - Anda tidak dapat menjalankan kode async pada utas terpisah - saat itu dan baru Anda harus mempertimbangkan merangkul reentrancy.
Jika Anda menemukan diri Anda di sudut ini, saya akan merekomendasikan menggunakan sesuatu seperti
Dispatcher.PushFrame
untuk aplikasi WPF , mengulang denganApplication.DoEvents
untuk aplikasi WinForm, dan untuk kasus umum, saya sendiriAsyncContext.Run
.sumber
Main()
metode async tidak dapat dikompilasi; pada titik tertentu Anda harus menjembatani kesenjangan antara dunia sinkronisasi dan dunia async. Ini bukan " situasi yang sangat langka" , itu perlu dalam setiap program yang memanggil metode async. Tidak ada opsi untuk tidak "melakukan sinkronisasi-asinkron" , hanya opsi untuk mengurangi beban itu hingga metode pemanggilan alih-alih memanggulnya dalam metode yang sedang Anda tulis.async
semua metode dalam aplikasi saya sekarang. Dan itu banyak. Tidak bisakah ini hanya default?Jika saya membaca pertanyaan Anda dengan benar - kode yang ingin panggilan sinkron ke metode async dijalankan pada utas dispatcher yang ditangguhkan. Dan Anda ingin benar-benar secara bersamaan memblokir utas itu sampai metode async selesai.
Metode Async di C # 5 didukung oleh secara efektif memotong metode menjadi potongan-potongan di bawah tenda, dan mengembalikan
Task
yang dapat melacak penyelesaian keseluruhan shabang keseluruhan. Namun, bagaimana metode cacah mengeksekusi dapat bergantung pada jenis ekspresi yang diteruskan keawait
operator.Sebagian besar waktu, Anda akan menggunakan
await
ekspresi tipeTask
. Implementasi Task dariawait
pola adalah "pintar" karena tidak sesuai denganSynchronizationContext
, yang pada dasarnya menyebabkan hal berikut terjadi:await
berada pada utas loop pesan Dispatcher atau WinForms, itu memastikan bahwa potongan metode async terjadi sebagai bagian dari pemrosesan antrian pesan.await
berada pada utas pool ulir, maka potongan yang tersisa dari metode async terjadi di mana saja pada utas pool.Itu sebabnya Anda mungkin mengalami masalah - implementasi metode async mencoba menjalankan sisanya pada Dispatcher - meskipun ditangguhkan.
.... mendukung! ....
Saya harus mengajukan pertanyaan, mengapa Anda mencoba untuk memblokir metode async secara sinkron? Melakukan hal itu akan mengalahkan tujuan mengapa metode ingin dipanggil secara tidak sinkron. Secara umum, ketika Anda mulai menggunakan
await
metode Dispatcher atau UI, Anda ingin mengubah seluruh aliran UI Anda sebagai sinkronisasi. Misalnya, jika callstack Anda mirip dengan yang berikut:WebRequest.GetResponse()
YourCode.HelperMethod()
YourCode.AnotherMethod()
YourCode.EventHandlerMethod()
[UI Code].Plumbing()
-WPF
atauWinForms
KodeWPF
atauWinForms
Loop PesanKemudian setelah kode diubah untuk menggunakan async, biasanya Anda akan berakhir dengan
WebRequest.GetResponseAsync()
YourCode.HelperMethodAsync()
YourCode.AnotherMethodAsync()
YourCode.EventHandlerMethodAsync()
[UI Code].Plumbing()
-WPF
atauWinForms
KodeWPF
atauWinForms
Loop PesanSebenarnya Menjawab
Kelas AsyncHelpers di atas benar-benar berfungsi karena berperilaku seperti loop pesan bersarang, tetapi menginstal mekanik paralelnya sendiri ke Dispatcher daripada mencoba mengeksekusi pada Dispatcher itu sendiri. Itu satu solusi untuk masalah Anda.
Solusi lain adalah dengan mengeksekusi metode async Anda pada utas threadpool, dan kemudian menunggu sampai selesai. Melakukannya mudah - Anda dapat melakukannya dengan cuplikan berikut:
API terakhir adalah Task.Run (...), tetapi dengan CTP Anda akan membutuhkan akhiran Ex ( penjelasan di sini ).
sumber
TaskEx.RunEx(GetCustomers).Result
menggantung aplikasi saat dijalankan pada utas dispatcher yang ditangguhkan. Juga, metode GetCustomers () biasanya dijalankan async, namun dalam satu situasi perlu dijalankan secara sinkron, jadi saya mencari cara untuk melakukannya tanpa membangun versi sinkronisasi dari metode tersebut.async
metode dengan benar ; loop bersarang tentu harus dihindari.Ini bekerja dengan baik untuk saya
sumber
MyAsyncMethod().RunTaskSynchronously();
Cara paling sederhana yang saya temukan untuk menjalankan tugas secara sinkron dan tanpa memblokir utas UI adalah dengan menggunakan RunSynchronously () seperti:
Dalam kasus saya, saya memiliki peristiwa yang menyala ketika sesuatu terjadi. Saya tidak tahu berapa kali itu akan terjadi. Jadi, saya menggunakan kode di atas dalam acara saya, jadi setiap kali menyala, ia membuat tugas. Tugas dieksekusi secara serempak dan itu bekerja dengan baik untuk saya. Saya hanya terkejut bahwa butuh waktu lama bagi saya untuk mengetahui hal ini mengingat betapa sederhananya itu. Biasanya, rekomendasi jauh lebih kompleks dan rawan kesalahan. Ini sederhana dan bersih.
sumber
Saya sudah menghadapinya beberapa kali, kebanyakan dalam pengujian unit atau dalam pengembangan layanan windows. Saat ini saya selalu menggunakan fitur ini:
Sederhana, mudah, dan saya tidak punya masalah.
sumber
Saya menemukan kode ini di komponen Microsoft.AspNet.Identity.Core, dan berfungsi.
sumber
Hanya sedikit catatan - pendekatan ini:
bekerja untuk WinRT.
Biarkan saya jelaskan:
Selain itu pendekatan ini hanya berfungsi untuk solusi Windows Store!
Catatan: Cara ini tidak aman untuk thread jika Anda memanggil metode Anda di dalam metode async lainnya (menurut komentar dari @Servy)
sumber
CancellationToken
solusi saya.Dalam kode Anda, tugas tunggu pertama Anda untuk dieksekusi tetapi Anda belum memulainya sehingga menunggu tanpa batas. Coba ini:
Edit:
Anda mengatakan bahwa Anda mendapatkan pengecualian. Silakan kirim detail lebih lanjut, termasuk jejak tumpukan.
Mono berisi test case berikut:
Periksa apakah ini cocok untuk Anda. Jika tidak, meskipun sangat tidak mungkin, Anda mungkin memiliki CTP Async build yang aneh. Jika berhasil, Anda mungkin ingin memeriksa apa yang sebenarnya dihasilkan oleh kompiler dan bagaimana
Task
instantiasi berbeda dari sampel ini.Edit # 2:
Aku memeriksa dengan reflektor bahwa pengecualian Anda dijelaskan terjadi ketika
m_action
adalahnull
. Ini agak aneh, tapi saya bukan ahli CTP Async. Seperti yang saya katakan, Anda harus mendekompilasi kode Anda dan melihat bagaimana sebenarnyaTask
sedang dipakai bagaimana punm_action
itunull
.PS Apa masalahnya dengan downvotes sesekali? Peduli menguraikan?
sumber
RunSynchronously may not be called on a task unbound to a delegate
. Google tidak membantu karena semua hasil untuk itu dalam bahasa Cina ...await
kata kunci digunakan. Pengecualian yang diposting dalam komentar saya sebelumnya adalah pengecualian yang saya dapatkan, meskipun itu adalah salah satu dari sedikit yang saya tidak bisa Google dan menemukan penyebab atau resolusi untuk.async
danasync
kata kunci tidak lebih dari gula sintaksis. Compiler menghasilkan kode untuk membuatTask<Customer>
diGetCustomers()
jadi itulah di mana saya akan melihat pertama. Adapun pengecualian, Anda hanya memposting pesan pengecualian, yang tidak berguna tanpa tipe pengecualian dan jejak stack. SebutToString()
metode pengecualian dan poskan output dalam pertanyaan.Diuji dalam .Net 4.6. Itu juga dapat menghindari kebuntuan.
Untuk metode async kembali
Task
.Untuk metode async kembali
Task<T>
Edit :
Jika pemanggil berjalan di utas kumpulan utas (atau penelepon juga dalam tugas), itu mungkin masih menyebabkan kebuntuan dalam beberapa situasi.
sumber
Result
sangat cocok untuk pekerjaan itu jika Anda menginginkan panggilan sinkron, dan sebaliknya berbahaya. Tidak ada dalam namaResult
atau intellisenseResult
yang menunjukkan itu adalah panggilan yang memblokir. Itu benar-benar harus diganti namanya.gunakan snip kode di bawah ini
sumber
Mengapa tidak membuat panggilan seperti:
itu bukan async.
sumber
Jawaban ini dirancang untuk siapa saja yang menggunakan WPF untuk .NET 4.5.
Jika Anda mencoba mengeksekusi
Task.Run()
di utas GUI, makatask.Wait()
akan hang tanpa batas, jika Anda tidak memilikiasync
kata kunci dalam definisi fungsi Anda.Metode ekstensi ini memecahkan masalah dengan memeriksa untuk melihat apakah kita berada di utas GUI, dan jika demikian, menjalankan tugas pada utas pengirim WPF.
Kelas ini dapat bertindak sebagai perekat antara dunia async / await dan dunia non-async / await, dalam situasi di mana ia tidak dapat dihindari, seperti properti MVVM atau dependensi pada API lain yang tidak menggunakan async / tunggu.
sumber
Cukup menelepon
.Result;
atau.Wait()
merupakan risiko untuk kebuntuan seperti yang banyak dikatakan dalam komentar. Karena sebagian besar dari kita suka oneliners Anda dapat menggunakannya untuk.Net 4.5<
Memperoleh nilai melalui metode async:
Sinkron memanggil metode async
Tidak ada masalah jalan buntu yang akan terjadi karena penggunaan
Task.Run
.Sumber:
https://stackoverflow.com/a/32429753/3850405
sumber
Saya pikir metode pembantu berikut ini juga bisa menyelesaikan masalah.
Dapat digunakan dengan cara berikut:
sumber
Ini bekerja untuk saya
sumber
Saya telah menemukan bahwa SpinWait bekerja dengan sangat baik untuk ini.
Pendekatan di atas tidak perlu menggunakan .Result atau. Tunggu (). Ini juga memungkinkan Anda menentukan batas waktu agar Anda tidak terjebak selamanya jika tugas tidak pernah selesai.
sumber
Pada wp8:
Bungkus itu:
Sebut saja:
sumber
sumber
Atau Anda bisa pergi dengan:
Agar ini dikompilasi, pastikan Anda merujuk perakitan ekstensi:
sumber
Coba kode berikut ini berfungsi untuk saya:
sumber