Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
Bu öğreticide, çok iş parçacıklı bir uygulamada hata ayıklamak için Paralel Yığınlar pencerelerininİş Parçacıkları görünümünün nasıl kullanılacağı gösterilmektedir. Bu pencere, çok iş parçacıklı kodun çalışma zamanı davranışını anlamanıza ve doğrulamanıza yardımcı olur.
C#, C++ ve Visual Basic için İş Parçacıkları Görünümü desteklenir. C# ve C++ için örnek kod sağlanır, ancak bazı kod başvuruları ve çizimleri yalnızca C# örnek kodu için geçerlidir.
İş Parçacıkları görünümü şunları oluşturmanıza yardımcı olur:
Birden çok iş parçacığı için çağrı yığını görselleştirmelerini görüntüleyerek, yalnızca geçerli iş parçacığının çağrı yığınını gösteren Çağrı Yığını penceresinden daha kapsamlı bir uygulama durumu elde edin.
Engellenen veya kilitlenmiş iş parçacıkları gibi sorunları tanımlamaya yardımcı olun.
Çok iş parçacıklı çağrı yığınları
Çağrı yığınının aynı bölümleri, karmaşık uygulamalar için görselleştirmeyi basitleştirmek üzere birlikte gruplandırılır.
Aşağıdaki kavramsal animasyon, gruplandırma işleminin çağrı yığınlarına nasıl uygulandığını gösterir. Çağrı yığınının yalnızca aynı kesimleri gruplandırılır. İş parçacıklarını tanımlamak için gruplandırılmış çağrı yığınının üzerine gelin.
Örnek koda genel bakış (C#, C++)
Bu kılavuzdaki örnek kod, bir gorilin hayatında bir gün simülasyonu sağlayan bir uygulamaya yöneliktir. Alıştırmanın amacı, çok iş parçacıklı bir uygulamada hata ayıklamak için Paralel Yığınlar penceresinin İş Parçacıkları görünümünün nasıl kullanılacağını anlamaktır.
Örnek, iki iş parçacığı birbirini beklerken oluşan bir kilitlenme durumu örneğini içerir.
Çağrı yığınını sezgisel hale getirmek için örnek uygulama aşağıdaki sıralı adımları gerçekleştirir:
- Gorilleri temsil eden bir nesne oluşturur.
- Goril uyanır.
- Goril sabah yürüyüşüne çıkıyor.
- Goril ormanda muz bulur.
- Goril yiyor.
- Goril maymun işine girer.
Örnek projeyi oluşturma
Projeyi oluşturmak için:
Visual Studio'yu açın ve yeni bir proje oluşturun.
Başlangıç penceresi açık değilse Dosya
Başlangıç Penceresi seçin. Başlangıç penceresinde Yeni proje'yi seçin.
Yeni proje oluştur penceresinde, arama kutusuna konsol girin veya yazın. Ardından, Dil listesinden C# veya C++ öğesini seçin ve ardından Platform listesinden Windows'u seçin.
Dil ve platform filtrelerini uyguladıktan sonra, seçtiğiniz dil için Konsol Uygulaması'nı ve ardından İleri'yi seçin.
Note
Doğru şablonu görmüyorsanız Visual Studio Yükleyicisi'ni açan Araçlar>Araç ve Özellik Al... seçeneğine gidin. .NET masaüstü geliştirme iş yükünü seçin ve ardından Değiştir'iseçin.
Yeni projenizi yapılandırın penceresinde, proje adı kutusuna bir ad yazın veya varsayılan adı kullanın. Ardından İleri'yi seçin.
.NET projesi için önerilen hedef çerçeveyi veya .NET 8'i ve ardından Oluştur'u seçin.
Yeni bir konsol projesi görünür. Proje oluşturulduktan sonra bir kaynak dosya görüntülenir.
Projede .cs (veya .cpp) kod dosyasını açın. Boş bir kod dosyası oluşturmak için içeriğini silin.
Seçtiğiniz dil için aşağıdaki kodu boş kod dosyasına yapıştırın.
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"); } } }Dosya menüsünde Tümünü Kaydet'i seçin.
Derle menüsünde Çözüm Derle'yi seçin.
Paralel Yığınlar penceresinin İş Parçacıkları Görünümünü kullanın
Hata ayıklamayı başlatmak için:
Hata Ayıkla menüsünde Hata Ayıklamayı Başlat 'ı (veya F5) seçin ve ilkin
Debugger.Break()isabetini bekleyin.Note
C++'ta hata ayıklayıcı
__debug_break()içinde duraklatılır. Bu makaledeki kod başvurularının ve çizimlerinin geri kalanı C# sürümüne yöneliktir, ancak aynı hata ayıklama ilkeleri C++ için de geçerlidir.F5 tuşuna bir kez bastığınızda hata ayıklayıcı aynı
Debugger.Break()satırda yeniden duraklatılır.Bu, ikinci iş parçacığında gerçekleşen ikinci çağrı sırasında
Gorilla_Startduraklatılır.Tip
Hata ayıklayıcısı iş parçacığı başına koda girer. Örneğin, yürütmeye devam etmek için F5 tuşuna basarsanız ve uygulama bir sonraki kesme noktasına ulaştığında, farklı bir iş parçacığındaki koda geçebilir. Hata ayıklama amacıyla bu davranışı yönetmeniz gerekiyorsa, ek kesme noktaları, koşullu kesme noktaları ekleyebilir veya Tümünü Kes'i kullanabilirsiniz. Koşullu kesme noktalarını kullanma hakkında daha fazla bilgi için bkz. Koşullu kesme noktalarıyla tek bir iş parçacığını izleme.
Paralel Yığınlar penceresini açmak için Windows > Paralel Yığınlarında Hata Ayıkla'yı > seçin ve ardından penceredeki Görünüm açılan listesinden İş Parçacıkları'nı seçin.
İş Parçacıkları görünümünde, geçerli iş parçacığının yığın çerçevesi ve çağrı yolu mavi renkle vurgulanır. İş parçacığının şu anki konumu sarı okla gösterilmektedir.
Çağrı yığınının
Gorilla_Startetiketinin 2 İş Parçacığı olduğuna dikkat edin. F5'e en son bastığınızda başka bir iş parçacığı başlattınız. Karmaşık uygulamalarda basitleştirme için, aynı çağrı yığınları tek bir görsel gösterimde birlikte gruplandırılır. Bu, özellikle birçok iş parçacığına sahip senaryolarda karmaşık olabilecek bilgileri basitleştirir.Hata ayıklama sırasında dış kodun görüntülenip görüntülenmeyeceğini değiştirebilirsiniz. Özelliği değiştirmek için Dış Kodu Göster'i seçin veya temizleyin. Dış kod gösterirseniz, bu kılavuzu kullanmaya devam edebilirsiniz, ancak sonuçlarınız çizimlerden farklı olabilir.
F5 tuşuna yeniden basın ve hata ayıklayıcı
Debugger.Break()yöntemindekiMorningWalksatırında durur.Paralel Yığınlar penceresinde,
MorningWalkyönteminde mevcut yürütülen iş parçacığının konumu gösterilir.
Toplanmış çağrı yığını tarafından temsil edilen iki iş parçacığı hakkında bilgi almak için
MorningWalkyöntem üzerine gel.Geçerli iş parçacığı, Debug araç çubuğundaki İş Parçacığı listesinde de görünür.
Hata ayıklayıcısı bağlamını farklı bir iş parçacığına değiştirmek için İş Parçacığı listesini kullanabilirsiniz. Bu işlem geçerli yürütülen iş parçacığını değiştirmez, yalnızca hata ayıklayıcı bağlamını değiştirir.
Alternatif olarak, İş Parçacıkları görünümünde bir yönteme çift tıklayarak veya İş Parçacıkları görünümünde bir yönteme sağ tıklayıp Çerçeveye> Geç[iş parçacığı kimliği] seçeneğini belirleyerek hata ayıklayıcısı bağlamını değiştirebilirsiniz.
F5 tuşuna yeniden bastığınızda hata ayıklayıcı, ikinci iş parçacığının
MorningWalkyönteminde duraklatılır.
İş parçacığı yürütme zamanlamasına bağlı olarak, bu noktada ayrı veya gruplandırılmış çağrı yığınları görürsünüz.
Yukarıdaki çizimde, iki iş parçacığının çağrı yığınları kısmen gruplandırılmıştır. Çağrı yığınlarının özdeş kesimleri gruplandırılır ve ok çizgileri ayrılmış olan kesimleri (yani aynı değil) işaret eder. Geçerli yığın çerçevesi mavi vurgulamayla gösterilir.
F5 tuşuna yeniden bastığınızda uzun bir gecikme yaşandığını ve İş Parçacıkları görünümünün herhangi bir çağrı yığını bilgisi göstermediğini görürsünüz.
Gecikme, kilitlenmeden kaynaklanır. İş parçacıkları engellenmiş olsa bile, hataları ayıklama işleminin durakta durmadığınız için İş Parçacıkları görünümünde hiçbir şey görünmez.
Note
C++'ta,
abort()çağrıldığına dair bir debug hatası da görürsünüz.Tip
Kilitlenme oluşursa veya tüm iş parçacıkları şu anda engellenirse Tümünü Durdur düğmesi çağrı yığını bilgilerini almak için etkili bir yöntemdir.
Hata Ayıklama araç çubuğundaki IDE'nin üst kısmında Tümünü Kes düğmesini seçin (duraklatma simgesi) veya Ctrl + Alt + Break tuşlarını kullanın.
İş Parçacıkları görünümündeki çağrı yığını üst kısmında
FindBananas'in kilitlendiği gösteriliyor. içindekiFindBananasyürütme işaretçisi, geçerli hata ayıklayıcısı bağlamını gösteren kıvrılmış yeşil bir ok olsa da, iş parçacıklarının şu anda çalışmadığını da bildirir.Note
C++'de faydalı "kilitlenme algılandı" bilgilerini ve simgelerini göremezsiniz. Ancak, yine de kilitlenmenin konumunu işaret eden kıvrılmış yeşil oku
Jungle.FindBananasiçinde bulursunuz.Kod düzenleyicisinde
lockişlevinde eğik yeşil oku buluyoruz. İki iş parçacığı,lockyöntemindekiFindBananasişlevinde engelleniyor.İş parçacığı yürütme sırasına bağlı olarak, kilitlenme ya
lock(tree)ya dalock(banana_bunch)deyiminde görünür.lockçağrısı,FindBananasyöntemindeki iş parçacıklarını engeller. Bir iş parçacığıtreeüzerindeki kilidin diğer iş parçacığı tarafından serbest bırakılmasını bekliyor, ancak diğer iş parçacığıbanana_bunchüzerindeki kilidi bırakabilmek içintreeüzerindeki kilidin serbest bırakılmasını bekliyor. Bu, iki iş parçacığı birbirini beklerken oluşan klasik bir kilitlenme örneğidir.Copilot kullanıyorsanız olası kilitlenmeleri tanımlamaya yardımcı olmak için yapay zeka tarafından oluşturulan iş parçacığı özetlerini de alabilirsiniz.
Örnek kodu düzeltme
Bu kodu düzeltmek için, her zaman tüm iş parçacıklarında tutarlı ve evrensel bir sırayla birden çok kilidi temin edin. Bu, döngüsel beklemeleri önler ve kilitlenmeleri ortadan kaldırır.
Kilitlenmeyi düzeltmek için içindeki
MorningWalkkodu aşağıdaki kodla değiştirin.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; }Uygulamayı yeniden başlatın.
Summary
Bu kılavuzda Parallel Stacks hata ayıklayıcısı penceresi gösterilmiştir. Çok iş parçacıklı kod kullanan gerçek projelerde bu pencereyi kullanın. C++, C# veya Visual Basic ile yazılmış paralel kodu inceleyebilirsiniz.