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 Utas jendela Parallel Stacks untuk men-debug aplikasi multithread. Jendela ini membantu Anda memahami dan memverifikasi perilaku run-time kode multithreaded.
Tampilan Utas didukung untuk C#, C++, dan Visual Basic. Kode sampel disediakan untuk C# dan C++, tetapi beberapa referensi kode dan ilustrasi hanya berlaku untuk kode sampel C#.
Tampilan Utas membantu Anda:
Lihat visualisasi tumpukan panggilan untuk beberapa utas, yang memberikan gambaran yang lebih lengkap tentang status aplikasi Anda daripada jendela Call Stack, yang hanya menampilkan tumpukan panggilan untuk utas saat ini.
Membantu mengidentifikasi masalah seperti utas yang diblokir atau terkunci mati.
Tumpukan panggilan berutas ganda
Bagian identik dari tumpukan panggilan dikelompokkan bersama untuk menyederhanakan visualisasi untuk aplikasi yang kompleks.
Animasi konseptual berikut menunjukkan bagaimana pengelompokan diterapkan ke tumpukan panggilan. Hanya segmen identik dari tumpukan panggilan yang dikelompokkan. Arahkan mouse ke tumpukan panggilan yang dikelompokkan untuk mengidenitfy utas.
Gambaran umum kode sampel (C#, C++)
Kode sampel dalam panduan ini adalah untuk aplikasi yang mensimulasikan sehari dalam kehidupan gorila. Tujuan latihan ini adalah untuk memahami cara menggunakan tampilan Utas dari jendela Parallel Stacks untuk men-debug aplikasi multithread.
Sampel mencakup contoh kebuntuan, yang terjadi ketika dua utas saling menunggu.
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
Untuk membuat proyek:
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# atau C++ dari daftar Bahasa, lalu pilih Windows dari daftar Platform.
Setelah Anda menerapkan filter bahasa dan platform, pilih Aplikasi Konsol untuk bahasa yang Anda pilih, lalu pilih Berikutnya.
Note
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 proyek .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 (atau .cpp) 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 Multithreaded_Deadlock { class Jungle { public static readonly object tree = new object(); public static readonly object banana_bunch = new object(); public static Barrier barrier = new Barrier(2); public static int FindBananas() { // Lock tree first, then banana lock (tree) { lock (banana_bunch) { Console.WriteLine("Got bananas."); return 0; } } } static void Gorilla_Start(object lockOrderObj) { Debugger.Break(); bool lockTreeFirst = (bool)lockOrderObj; Gorilla koko = new Gorilla(lockTreeFirst); int result = 0; var done = new ManualResetEventSlim(false); Thread t = new Thread(() => { result = koko.WakeUp(); done.Set(); }); t.Start(); done.Wait(); } static void Main(string[] args) { List<Thread> threads = new List<Thread>(); // Start two threads with opposite lock orders threads.Add(new Thread(Gorilla_Start)); threads[0].Start(true); // First gorilla locks tree then banana threads.Add(new Thread(Gorilla_Start)); threads[1].Start(false); // Second gorilla locks banana then tree foreach (var t in threads) { t.Join(); } } } class Gorilla { private readonly bool lockTreeFirst; public Gorilla(bool lockTreeFirst) { this.lockTreeFirst = lockTreeFirst; } public int WakeUp() { int myResult = MorningWalk(); return myResult; } public int MorningWalk() { Debugger.Break(); if (lockTreeFirst) { lock (Jungle.tree) { Jungle.barrier.SignalAndWait(5000); // For thread timing consistency in sample Jungle.FindBananas(); GobbleUpBananas(); } } else { lock (Jungle.banana_bunch) { Jungle.barrier.SignalAndWait(5000); // For thread timing consistency in sample Jungle.FindBananas(); GobbleUpBananas(); } } return 0; } public void GobbleUpBananas() { Console.WriteLine("Trying to gobble up food..."); DoSomeMonkeyBusiness(); } public void DoSomeMonkeyBusiness() { Thread.Sleep(1000); Console.WriteLine("Monkey business done"); } } }Pada menu File, pilih Simpan Semua.
Dari menu Buat, pilih Buat Solusi.
Menggunakan tampilan Utas dari jendela Tumpukan Paralel
Untuk memulai debugging:
Pada menu Debug , pilih Mulai Penelusuran Kesalahan (atau F5) dan tunggu hingga yang pertama
Debugger.Break()tertemu.Note
Di C++, debugger berhenti sejenak di
__debug_break(). Referensi dan ilustrasi kode lainnya dalam artikel ini adalah untuk versi C#, tetapi prinsip penelusuran kesalahan yang sama berlaku untuk C++.Tekan F5 sekali, dan debugger berhenti lagi pada baris yang sama
Debugger.Break().Jeda terjadi dalam panggilan kedua di
Gorilla_Start, yang terjadi di utas kedua.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 perilaku ini untuk tujuan penelusuran kesalahan, Anda dapat menambahkan titik henti tambahan, titik henti kondisional, atau menggunakan Break All. Untuk informasi selengkapnya tentang cara menggunakan titik henti kondisional, lihat Mengikuti satu utas dengan titik henti kondisional.
Pilih Debug > Tumpukan Paralel Windows > untuk membuka jendela Tumpukan Paralel, lalu pilih Utas dari menu dropdown Tampilan di jendela.
Dalam tampilan Utas, kerangka tumpukan dan jalur panggilan utas saat ini disorot dengan warna biru. Lokasi utas saat ini diperlihatkan oleh panah kuning.
Perhatikan label untuk tumpukan panggilan untuk
Gorilla_Startadalah 2 Thread. Ketika Anda terakhir kali menekan F5, Anda memulai satu utas lagi. Untuk penyederhanaan dalam aplikasi kompleks, tumpukan panggilan yang identik dikelompokkan bersama ke dalam satu representasi visual. Ini menyederhanakan informasi yang berpotensi kompleks, terutama dalam skenario dengan banyak utas.Selama debugging, Anda dapat beralih penampilan kode eksternal. Untuk mengalihkan fitur, 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 pada baris
Debugger.Break()dalam metodeMorningWalk.Jendela Parallel Stacks menunjukkan lokasi utas eksekusi saat ini dalam
MorningWalkmetode .
Letakkan kursor di atas metode
MorningWalkuntuk mendapatkan informasi tentang dua utas yang diwakili oleh tumpukan panggilan yang dikelompokkan.Utas saat ini juga muncul di daftar Utas di toolbar Debug.
Anda dapat menggunakan daftar Utas untuk mengalihkan konteks debugger ke utas yang berbeda. Ini tidak mengubah utas eksekusi saat ini, melainkan hanya mengubah konteks debugger.
Atau, Anda dapat mengubah konteks debugger dengan mengeklik dua kali metode di tampilan Utas, atau dengan mengklik kanan metode di tampilan Utas dan memilih Beralih ke Frame>[ID utas].
Tekan F5 lagi dan debugger berhenti pada metode
MorningWalkuntuk utas kedua.
Bergantung pada waktu eksekusi utas, pada titik ini Anda dapat melihat tumpukan panggilan terpisah atau dikelompokkan.
Pada ilustrasi sebelumnya, tumpukan panggilan dari dua utas tersebut dikelompokkan secara parsial. Segmen identik dari tumpukan panggilan dikelompokkan, dan garis panah menunjuk ke segmen yang dipisahkan (yaitu, tidak identik). Bingkai tumpukan saat ini ditandai dengan penyorotan biru.
Tekan F5 lagi, dan Anda akan melihat penundaan panjang terjadi dan tampilan Utas tidak menampilkan informasi tumpukan panggilan apa pun.
Penundaan ini disebabkan oleh kebuntuan. Tidak ada yang muncul di tampilan Utas karena meskipun utas mungkin diblokir, Anda saat ini tidak dihentikan sementara di debugger.
Note
Di C++, Anda juga melihat kesalahan debug yang menunjukkan bahwa
abort()telah dipanggil.Tip
Tombol Putuskan Semua adalah cara yang baik untuk mendapatkan informasi tumpukan panggilan jika terjadi deadlock atau semua utas sedang diblokir.
Di bagian atas IDE di toolbar Debug, pilih tombol Hentian Semua (ikon jeda), atau gunakan Ctrl + Alt + Break.
Bagian atas tumpukan panggilan dalam tampilan Utas memperlihatkan yang
FindBananasdi-deadlock. Penunjuk eksekusi diFindBananasadalah panah hijau melengkung, menunjukkan konteks debugger saat ini tetapi juga memberi tahu kita bahwa utas saat ini tidak berjalan.Note
Di C++, Anda tidak melihat informasi dan ikon "kebuntuan" yang berguna terdeteksi. Namun, Anda masih menemukan panah hijau melengkung di
Jungle.FindBananas, mengisyaratkan lokasi kebuntuan.Di editor kode, kita menemukan panah hijau melengkung dalam
lockfungsi . Dua utas diblokir pada fungsilockdalam metodeFindBananas.Tergantung pada urutan eksekusi utas, kebuntuan muncul dalam pernyataan
lock(tree)ataulock(banana_bunch).Panggilan untuk
lockmemblokir utas dalam metodeFindBananas. Satu utas menunggu kunci padatreedilepaskan oleh utas lain, tetapi utas lainnya menunggu kunci padabanana_bunchdilepaskan dulu sebelum dapat melepaskan kunci padatree. Ini adalah contoh kebuntuan klasik yang terjadi ketika dua utas saling menunggu.Jika Anda menggunakan Copilot, Anda juga bisa mendapatkan ringkasan utas yang dihasilkan AI untuk membantu mengidentifikasi potensi kebuntuan.
Memperbaiki kode sampel
Untuk memperbaiki kode ini, selalu dapatkan beberapa kunci dalam urutan global yang konsisten di semua utas. Ini mencegah menunggu melingkar dan menghilangkan kebuntuan.
Untuk memperbaiki kebuntuan, ganti kode di
MorningWalkdengan kode berikut.public int MorningWalk() { Debugger.Break(); // Always lock tree first, then banana_bunch lock (Jungle.tree) { Jungle.barrier.SignalAndWait(5000); // OK to remove lock (Jungle.banana_bunch) { Jungle.FindBananas(); GobbleUpBananas(); } } return 0; }Mulai ulang aplikasi.
Summary
Panduan ini menunjukkan jendela debugger Parallel Stacks . Gunakan jendela ini pada proyek nyata yang menggunakan kode multithreaded. Anda dapat memeriksa kode paralel yang ditulis dalam C++, C#, atau Visual Basic.