Bila hendak menggunakan Task.WaitAll vs. Task.WhenAll in .NET

TPL (Task Parallel Library) adalah salah satu ciri baru paling menarik yang ditambahkan dalam versi .NET framework terbaru. Kaedah Task.WaitAll dan Task.WhenAll adalah dua kaedah penting dan sering digunakan dalam TPL.

The Task.WaitAll menyekat utas semasa sehingga semua tugas lain selesai dilaksanakan. Kaedah Task.WhenAll digunakan untuk membuat tugas yang akan selesai jika dan hanya jika semua tugas lain telah selesai.

Oleh itu, jika anda menggunakan Task.WhenAll anda akan mendapat objek tugas yang tidak lengkap. Walau bagaimanapun, ia tidak akan menyekat tetapi akan membolehkan program tersebut dijalankan. Sebaliknya, panggilan kaedah Task.WaitAll sebenarnya menyekat dan menunggu semua tugas lain diselesaikan.

Pada dasarnya, Task.WhenAll akan memberi anda tugas yang tidak lengkap, tetapi anda boleh menggunakan ContinueWith sebaik sahaja tugas-tugas yang ditentukan selesai pelaksanaannya. Perhatikan bahawa Task.WhenAll atau Task.WaitAll sebenarnya tidak akan menjalankan tugas; iaitu, tidak ada tugas yang dimulakan dengan kaedah ini. Inilah cara ContinueWith digunakan dengan Task.WhenAll: 

Task.WhenAll (taskList) .ContinueWith (t => {

  // tulis kod anda di sini

});

Seperti yang dinyatakan oleh dokumentasi Microsoft, Task.WhenAll "membuat tugas yang akan selesai apabila semua objek Task dalam koleksi yang banyak dapat diselesaikan."

Task.WhenAll vs. Task.WaitAll

Izinkan saya menjelaskan perbezaan antara kedua kaedah ini dengan contoh yang mudah. Katakan anda mempunyai tugas yang melakukan beberapa aktiviti dengan utas UI - katakanlah, beberapa animasi perlu ditunjukkan di antara muka pengguna. Sekarang, jika anda menggunakan Task.WaitAll, antara muka pengguna akan disekat dan tidak akan dikemas kini sehingga semua tugas yang berkaitan selesai dan blok dilepaskan. Walau bagaimanapun, jika anda menggunakan Task.WhenAll dalam aplikasi yang sama, thread UI tidak akan disekat dan akan dikemas kini seperti biasa.

Oleh itu, kaedah mana yang harus anda gunakan bila? Anda boleh menggunakan WaitAll ketika niat menyekat secara serentak untuk mendapatkan hasilnya. Tetapi apabila anda ingin memanfaatkan asinkron, anda mahu menggunakan varian WhenAll. Anda boleh menunggu Task.WhenAll tanpa perlu menyekat thread semasa. Oleh itu, anda mungkin mahu menggunakan menunggu dengan Task.WhenAll dalam kaedah async.

Semasa Task.WaitAll menyekat utas semasa sehingga semua tugas yang belum selesai selesai, Task.WhenAll mengembalikan objek tugas. Task.WaitAll melemparkan AggregateException apabila satu atau lebih tugas membuang pengecualian. Apabila satu atau lebih tugas membuang pengecualian dan anda menunggu kaedah Task.WhenAll, ia membuka AggregateException dan mengembalikan yang pertama.

Elakkan menggunakan Task. Jalankan dalam gelung

Anda boleh menggunakan tugas ketika anda ingin melakukan aktiviti bersamaan. Sekiranya anda memerlukan tahap selari yang tinggi, tugas tidak pernah menjadi pilihan yang baik. Selalu disarankan untuk tidak menggunakan benang kolam renang di ASP.Net. Oleh itu, anda harus menahan diri daripada menggunakan Task.Run atau Task.factory.StartNew di ASP.Net.

Task.Run harus selalu digunakan untuk kod terikat CPU. Task.Run bukanlah pilihan yang baik dalam aplikasi ASP.Net, atau, aplikasi yang memanfaatkan runtime ASP.Net kerana ia hanya memuatkan kerja ke thread ThreadPool. Sekiranya anda menggunakan ASP.Net Web API, permintaan itu akan menggunakan ThreadPool thread. Oleh itu, jika anda menggunakan Task.Run dalam aplikasi API Web ASP.Net anda, anda hanya mengehadkan skalabiliti dengan memuatkan kerja ke utas pekerja lain tanpa sebab.

Perhatikan bahawa terdapat kelemahan dalam menggunakan Task. Jalankan dalam satu gelung. Sekiranya anda menggunakan kaedah Task.Run dalam satu gelung, beberapa tugas akan dibuat - satu untuk setiap unit kerja atau lelaran. Namun, jika anda menggunakan Parallel.ForEach sebagai ganti menggunakan Task.Jalankan dalam satu gelung, Partitioner akan dibuat untuk mengelakkan membuat lebih banyak tugas untuk melakukan aktiviti daripada yang diperlukan. Ini dapat meningkatkan prestasi dengan ketara kerana anda dapat mengelakkan terlalu banyak pertukaran konteks dan masih memanfaatkan banyak teras dalam sistem anda.

Harus diingat bahawa Parallel.ForEach menggunakan Partitioner secara dalaman untuk mengedarkan koleksi ke dalam item kerja. Secara kebetulan, pengedaran ini tidak berlaku untuk setiap tugas dalam senarai item, sebaliknya, ia berlaku sebagai kumpulan. Ini mengurangkan overhead yang terlibat dan dengan itu meningkatkan prestasi. Dengan kata lain, jika anda menggunakan Task.Run atau Task.Factory.StartNew di dalam gelung, mereka akan membuat tugas baru secara eksplisit untuk setiap lelaran dalam gelung. Parallel.ForEach jauh lebih efisien kerana ia akan mengoptimumkan pelaksanaan dengan mengagihkan beban kerja di beberapa teras dalam sistem anda.