Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Tutorial ini menunjukkan cara menggunakan tampilan Tugas jendela Parallel Stacks untuk men-debug aplikasi asinkron C#. Jendela ini membantu Anda memahami dan memverifikasi perilaku run-time kode yang menggunakan pola asinkron/tunggu, juga disebut pola asinkron berbasis Tugas (TAP).
Untuk aplikasi yang menggunakan Pustaka Paralel Tugas (TPL) tetapi bukan pola async/await, atau untuk aplikasi C++ menggunakan Runtime Konkurensi, gunakan tampilan Utas di jendela Tumpukan Paralel untuk debugging. Untuk informasi selengkapnya, lihat Men-debug kebuntuan dan Menampilkan utas dan tugas di jendela Tumpukan Paralel.
Tampilan Tugas membantu Anda:
Lihat visualisasi tumpukan panggilan untuk aplikasi yang menggunakan pola asinkron/tunggu. Dalam skenario ini, tampilan Tugas menyediakan gambaran yang lebih lengkap tentang status aplikasi Anda.
Identifikasi kode asinkron yang dijadwalkan untuk dijalankan tetapi belum berjalan. Misalnya, permintaan HTTP yang belum mengembalikan data apa pun lebih mungkin muncul dalam tampilan Tugas, alih-alih tampilan Threads, yang membantu Anda mengisolasi masalah.
Bantu identifikasi masalah seperti pola sinkronisasi di atas asinkron bersama dengan petunjuk terkait masalah potensial seperti tugas yang diblokir atau menunggu. Sync-over-async code pattern mengacu pada kode yang memanggil metode asinkron secara sinkron, yang diketahui memblokir utas dan merupakan penyebab paling umum dari kelaparan kumpulan utas.
Tumpukan panggilan asinkron
Tampilan Tugas di Parallel Stacks menyediakan visualisasi untuk tumpukan panggilan asinkron, sehingga Anda dapat melihat apa yang terjadi (atau seharusnya terjadi) di aplikasi Anda.
Berikut adalah beberapa poin penting yang perlu diingat saat menginterpretasikan data dalam tampilan Tugas.
Tumpukan panggilan asinkron adalah tumpukan panggilan logis atau virtual, bukan tumpukan panggilan fisik yang mewakili tumpukan. Saat bekerja dengan kode asinkron (misalnya, menggunakan
awaitkata kunci), debugger menyediakan tampilan "tumpukan panggilan asinkron", atau "tumpukan panggilan virtual". Tumpukan panggilan asinkron berbeda dari tumpukan panggilan berbasis utas, atau "tumpukan fisik", karena tumpukan panggilan asinkron belum tentu berjalan saat ini pada utas fisik apa pun. Sebaliknya, tumpukan panggilan asinkron adalah kelanjutan atau "janji" kode yang akan berjalan di masa depan, secara asinkron. Tumpukan panggilan dibuat menggunakan kelanjutan.Kode asinkron yang dijadwalkan tetapi saat ini tidak berjalan tidak muncul di tumpukan panggilan fisik, tetapi akan muncul pada tumpukan panggilan asinkron dalam tampilan Tugas. Jika Anda memblokir utas menggunakan metode seperti
.Waitatau.Result, Anda mungkin melihat kode di tumpukan panggilan fisik sebagai gantinya.Tumpukan panggilan virtual asinkron tidak selalu intuitif, karena percabangan yang dihasilkan dari penggunaan panggilan metode seperti
.WaitAnyatau.WaitAll.Jendela Call Stack mungkin berguna dalam kombinasi dengan tampilan Tugas, karena menampilkan tumpukan panggilan fisik untuk utas eksekusi saat ini.
Bagian identik dari tumpukan panggilan virtual dikelompokkan bersama untuk menyederhanakan visualisasi untuk aplikasi yang kompleks.
Animasi konseptual berikut menunjukkan bagaimana pengelompokan diterapkan ke tumpukan panggilan virtual. Hanya segmen identik dari tumpukan panggilan virtual yang dikelompokkan. Arahkan mouse ke tumpukan panggilan yang dikelompokkan untuk mengidenitfy utas yang menjalankan tugas.
Sampel C#
Kode sampel dalam panduan ini adalah untuk aplikasi yang mensimulasikan sehari dalam kehidupan gorila. Tujuan latihan adalah untuk memahami cara menggunakan tampilan Tugas dari jendela Parallel Stacks untuk men-debug aplikasi asinkron.
Sampel mencakup contoh penggunaan pola antipattern sinkronisasi di atas asinkron, yang dapat mengakibatkan kekurangan sumber daya kumpulan utas.
Untuk membuat tumpukan panggilan intuitif, aplikasi sampel melakukan langkah-langkah berurutan berikut:
- Membuat objek yang mewakili gorila.
- Gorila bangun.
- Gorila pergi berjalan pagi.
- Gorila menemukan pisang di hutan.
- Gorila makan.
- Gorila terlibat dalam bisnis monyet.
Membuat proyek sampel
Buka Visual Studio dan buat proyek baru.
Jika jendela Mulai tidak terbuka, pilih File>Jendela Mulai.
Pada jendela mulai, pilih Proyek baru.
Pada jendela Buat proyek baru , masukkan atau ketik konsol di kotak pencarian. Selanjutnya, pilih C# dari daftar Bahasa, lalu pilih Windows dari daftar Platform.
Setelah Anda menerapkan filter bahasa dan platform, pilih Aplikasi Konsol untuk .NET, lalu pilih Berikutnya.
Nota
Jika Anda tidak melihat templat yang benar, buka Alat>Dapatkan Alat dan Fitur..., yang membuka Alat Penginstal Visual Studio. Pilih beban kerja pengembangan desktop .NET, lalu pilih Ubah.
Di jendela Konfigurasikan proyek baru Anda , ketik nama atau gunakan nama default dalam kotak Nama proyek . Kemudian, pilih Berikutnya.
Untuk .NET, pilih kerangka kerja target yang direkomendasikan atau .NET 8, lalu pilih Buat.
Proyek konsol baru muncul. Setelah proyek dibuat, file sumber muncul.
Buka file kode .cs dalam proyek. Hapus isinya untuk membuat file kode kosong.
Tempelkan kode berikut untuk bahasa yang Anda pilih ke dalam file kode kosong.
using System.Diagnostics; namespace AsyncTasks_SyncOverAsync { class Jungle { public static async Task<int> FindBananas() { await Task.Delay(1000); Console.WriteLine("Got bananas."); return 0; } static async Task Gorilla_Start() { Debugger.Break(); Gorilla koko = new Gorilla(); int result = await Task.Run(koko.WakeUp); } static async Task Main(string[] args) { List<Task> tasks = new List<Task>(); for (int i = 0; i < 2; i++) { Task task = Gorilla_Start(); tasks.Add(task); } await Task.WhenAll(tasks); } } class Gorilla { public async Task<int> WakeUp() { int myResult = await MorningWalk(); return myResult; } public async Task<int> MorningWalk() { int myResult = await Jungle.FindBananas(); GobbleUpBananas(myResult); return myResult; } /// <summary> /// Calls a .Wait. /// </summary> public void GobbleUpBananas(int food) { Console.WriteLine("Trying to gobble up food synchronously..."); Task mb = DoSomeMonkeyBusiness(); mb.Wait(); } public async Task DoSomeMonkeyBusiness() { Debugger.Break(); while (!System.Diagnostics.Debugger.IsAttached) { Thread.Sleep(100); } await Task.Delay(30000); Console.WriteLine("Monkey business done"); } } }Setelah Memperbarui file kode, simpan perubahan Anda dan buat solusinya.
Pada menu File, pilih Simpan Semua.
Dari menu Buat, pilih Buat Solusi.
Menggunakan Tampilan Tugas dari jendela Tumpukan Paralel
Pada menu Debug , pilih Mulai Penelusuran Kesalahan (atau F5) dan tunggu hingga yang pertama
Debugger.Break()tertemu.Tekan F5 sekali, dan debugger berhenti lagi pada baris yang sama
Debugger.Break().Ini menjeda pada panggilan kedua ke
Gorilla_Start, yang terjadi di dalam tugas asinkron kedua.Pilih Debug > Tumpukan Paralel Windows > untuk membuka jendela Tumpukan Paralel, lalu pilih Tugas dari menu dropdown Tampilan di jendela.
Perhatikan label untuk tumpukan panggilan asinkron menyebutkan 2 Tumpukan Logis Asinkron. Ketika Anda terakhir kali menekan F5, Anda memulai tugas lain. Untuk penyederhanaan dalam aplikasi kompleks, tumpukan panggilan asinkron yang identik dikelompokkan bersama ke dalam satu representasi visual. Ini memberikan informasi yang lebih lengkap, terutama dalam skenario dengan banyak tugas.
Berbeda dengan tampilan Tugas, jendela Tumpukan Panggilan memperlihatkan tumpukan panggilan hanya untuk utas saat ini, bukan untuk beberapa tugas. Seringkali membantu untuk melihat keduanya bersama-sama untuk gambaran yang lebih lengkap tentang status aplikasi.
Tip
Jendela Call Stack dapat menampilkan informasi seperti kebuntuan, menggunakan deskripsi
Async cycle.Selama debugging, Anda dapat beralih penampilan kode eksternal. Untuk mengalihkan fitur, klik kanan header tabel Nama dari jendela Tumpukan Panggilan , lalu pilih atau hapus Perlihatkan Kode Eksternal. Jika Anda menampilkan kode eksternal, Anda masih dapat menggunakan panduan ini, tetapi hasil Anda mungkin berbeda dari ilustrasi.
Tekan F5 lagi, dan debugger berhenti di dalam metode
DoSomeMonkeyBusiness.
Tampilan ini menunjukkan tumpukan panggilan asinkron yang lebih lengkap setelah lebih banyak metode asinkron ditambahkan ke rantai kelanjutan internal, yang terjadi saat menggunakan
awaitdan metode serupa.DoSomeMonkeyBusinessmungkin atau mungkin tidak ada di bagian atas tumpukan panggilan asinkron karena merupakan metode asinkron tetapi belum ditambahkan ke rantai kelanjutan. Kami akan mengeksplorasi mengapa hal ini terjadi dalam langkah-langkah berikut.Tampilan ini juga memperlihatkan ikon yang diblokir untuk
Jungle.Main
. Ini informatif, tetapi biasanya tidak menunjukkan masalah. Tugas yang diblokir adalah tugas yang diblokir karena menunggu tugas lain selesai, peristiwa yang akan disinyalir, atau kunci yang akan dilepaskan.Arahkan kursor ke
GobbleUpBananasmetode untuk mendapatkan informasi tentang dua thread yang menjalankan tugas.
Utas saat ini juga muncul di daftar Utas di toolbar Debug.
Anda dapat menggunakan daftar Utas untuk mengalihkan konteks debugger ke utas yang berbeda.
Tekan F5 lagi dan debugger berhenti sejenak dalam
DoSomeMonkeyBusinessmetode untuk tugas kedua.
Bergantung pada waktu eksekusi tugas, pada titik ini Anda melihat tumpukan panggilan asinkron terpisah atau dikelompokkan.
Dalam ilustrasi sebelumnya, tumpukan panggilan asinkron untuk dua tugas terpisah karena tidak identik.
Tekan F5 lagi, dan Anda akan melihat penundaan panjang terjadi dan tampilan Tugas tidak menampilkan informasi tumpukan panggilan asinkron.
Penundaan disebabkan oleh tugas yang berjalan lama. Untuk tujuan contoh ini, ini mensimulasikan tugas yang berjalan lama seperti permintaan web, yang dapat mengakibatkan kelangkaan resources di kumpulan utas. Tidak ada yang muncul di tampilan Tugas karena meskipun tugas mungkin diblokir, saat ini Anda tidak sedang menjeda di debugger.
Tip
Tombol Putuskan Semua adalah cara yang baik untuk mendapatkan informasi stack panggilan jika terjadi kebuntuan atau saat semua tugas dan utas diblokir.
Di bagian atas IDE di toolbar Debug, pilih tombol Hentian Semua (ikon jeda), Ctrl + Alt + Break.
Di dekat bagian atas tumpukan panggilan asinkron dalam tampilan Tugas, Anda melihat bahwa
GobbleUpBananasdiblokir. Bahkan, dua tugas diblokir pada titik yang sama. Tugas yang diblokir belum tentu tidak terduga dan tidak selalu berarti ada masalah. Namun, keterlambatan eksekusi yang diamati menunjukkan masalah, dan informasi tumpukan panggilan di sini menunjukkan lokasi masalah.Di sisi kiri cuplikan layar sebelumnya, panah hijau melengkung menunjukkan konteks debugger saat ini. Dua tugas terblokir pada
mb.Wait()dalam metodeGobbleUpBananas.Jendela Call Stack juga menunjukkan bahwa utas saat ini diblokir.
Panggilan ke
Wait()memblokir utas dalam panggilan sinkron keGobbleUpBananas. Ini adalah contoh antipola sinkronisasi-di-atas-asinkron, dan jika ini terjadi pada utas UI atau dalam beban kerja pemrosesan yang besar, biasanya akan diatasi dengan perbaikan kode menggunakanawait. Untuk informasi selengkapnya, lihat Debug kelangkaan kumpulan utas. Untuk menggunakan alat profiling untuk men-debug kekurangan sumber daya pada kumpulan utas, lihat Studi kasus: Mengisolasi masalah kinerja.Menariknya,
DoSomeMonkeyBusinesstidak muncul pada tumpukan panggilan. Saat ini dijadwalkan, tidak berjalan, sehingga hanya muncul di tumpukan panggilan asinkron dalam tampilan Tugas.Tip
Debugger memasuki kode berdasarkan dasar per-utas. Misalnya, ini berarti bahwa jika Anda menekan F5 untuk melanjutkan eksekusi, dan aplikasi mencapai titik henti berikutnya, itu dapat memecah kode pada utas yang berbeda. Jika Anda perlu mengelola ini untuk tujuan penelusuran kesalahan, Anda dapat menambahkan titik henti tambahan, menambahkan titik henti kondisional, atau menggunakan Break All. Untuk informasi selengkapnya tentang perilaku ini, lihat Mengikuti satu utas dengan titik henti kondisional.
Memperbaiki kode sampel
GobbleUpBananasGanti metode dengan kode berikut.public async Task GobbleUpBananas(int food) // Previously returned void. { Console.WriteLine("Trying to gobble up food..."); //Task mb = DoSomeMonkeyBusiness(); //mb.Wait(); await DoSomeMonkeyBusiness(); }Dalam metode
MorningWalk, panggil GobbleUpBananas menggunakanawait.await GobbleUpBananas(myResult);Pilih tombol Hidupkan ulang (Ctrl + Shift + F5), lalu tekan F5 beberapa kali hingga aplikasi tampak "menggantung".
Tekan Putuskan Semua.
Kali ini,
GobbleUpBananasberjalan secara asinkron. Saat Anda istirahat, Anda akan melihat tumpukan panggilan asinkron.
Jendela Call Stack kosong kecuali untuk
ExternalCodeentri.Editor kode tidak menunjukkan apa pun kepada kami, kecuali menyediakan pesan yang menunjukkan bahwa semua utas menjalankan kode eksternal.
Namun, tampilan Tugas memberikan informasi yang berguna.
DoSomeMonkeyBusinessberada di bagian atas tumpukan panggilan asinkron, seperti yang diharapkan. Ini dengan benar memberi tahu kami di mana metode jangka panjang berada. Ini berguna untuk mengisolasi masalah asinkron/menunggu ketika tumpukan panggilan fisik di jendela Call Stack tidak memberikan detail yang cukup.
Ringkasan
Panduan ini menunjukkan jendela debugger Parallel Stacks . Gunakan jendela ini pada aplikasi yang menggunakan pola asinkron/tunggu.