Aracılığıyla paylaş


Zaman uyumsuz uygulamada hata ayıklama

Bu öğreticide, bir C# zaman uyumsuz uygulamasında hata ayıklamak için Paralel Yığınlar penceresinin Görevler görünümünün nasıl kullanılacağı gösterilmektedir. Bu pencere, Görev tabanlı zaman uyumsuz desen (TAP) olarak da adlandırılan zaman uyumsuz/await desenini kullanan kodun çalışma zamanı davranışını anlamanıza ve doğrulamanıza yardımcı olur.

Görev Paralel Kitaplığı'nı (TPL) kullanan ancak zaman uyumsuz/await desenini kullanmayan uygulamalar için veya Eşzamanlılık Çalışma Zamanı'nı kullanan C++ uygulamaları için, hata ayıklama için Paralel Yığınlar penceresindeki İş Parçacıkları görünümünü kullanın. Daha fazla bilgi için bkz. Kilitlenme hatalarını ayıklama ve Paralel Yığınlar penceresinde iş parçacıklarını ve görevleri görüntüleme.

Görevler görünümü şunları oluşturmanıza yardımcı olur:

  • Asenkron/await desenini kullanan uygulamalar için çağrı yığını görsellerini görüntüleyin. Bu senaryolarda Görevler görünümü, uygulama durumunuzun daha eksiksiz bir resmini sağlar.

  • Çalışmak üzere zamanlanmış ancak henüz çalışmayan eşzamansız kodu belirleyin. Örneğin, veri döndürmeyen bir HTTP isteğinin, sorunu yalıtmanıza yardımcı olan İş Parçacıkları görünümü yerine Görevler görünümünde görünme olasılığı daha yüksektir.

  • Eşzamanlı üstü eşzamansız (sync-over-async) düzeni gibi sorunların yanı sıra engellenmiş veya bekleyen görevler gibi potansiyel sorunlarla ilgili ipuçlarını belirlemeye yardımcı olun. Sync-over-async kod paterni, zaman uyumsuz yöntemleri zaman uyumlu bir şekilde çağıran koda atıfta bulunur. Bu kodun iş parçacıklarını engellediği bilinir ve iş parçacığı havuzu yetersizliğinin en yaygın nedenidir.

Asenkron çağrı yığınları

Paralel Yığınlar'daki Görevler görünümü, zaman uyumsuz çağrı yığınları için bir görselleştirme sağlar, böylece uygulamanızda neler olduğunu (veya olması gerekenleri) görebilirsiniz.

Görevler görünümünde verileri yorumlarken hatırlamanız gereken birkaç önemli nokta aşağıdadır.

  • Zaman uyumsuz çağrı yığınları, yığını temsil eden fiziksel çağrı yığınları değil mantıksal veya sanal çağrı yığınlarıdır. Zaman uyumsuz kodla çalışırken (örneğin anahtar sözcüğünü await kullanarak), hata ayıklayıcı "zaman uyumsuz çağrı yığınları" veya "sanal çağrı yığınları" görünümünü sağlar. Zaman uyumsuz çağrı yığınları şu anda herhangi bir fiziksel iş parçacığında çalıştırılmadığından, zaman uyumsuz çağrı yığınları iş parçacığı tabanlı çağrı yığınlarından veya "fiziksel yığınlardan" farklıdır. Bunun yerine, zaman uyumsuz çağrı yığınları, gelecekte zaman uyumsuz olarak çalışacak kodun devamlılıkları veya "vaatleri"dir. Çağrı yığınları devamlılıklar kullanılarak oluşturulur.

  • Zamanlanmış ancak şu anda çalışmayan zaman uyumsuz kod fiziksel çağrı yığınında görünmez, ancak Görevler görünümünde zaman uyumsuz çağrı yığınında görünmelidir. Eğer .Wait veya .Result gibi yöntemleri kullanarak iş parçacıklarını engelliyorsanız, kodu bunun yerine fiziksel çağrı yığınında görebilirsiniz.

  • Zaman uyumsuz sanal çağrı yığınları, .WaitAny veya .WaitAll gibi yöntem çağrılarının kullanımından kaynaklanan dallanma nedeniyle her zaman sezgisel değildir.

  • Çağrı Yığını penceresi, geçerli yürütülen iş parçacığı için fiziksel çağrı yığınını gösterdiğinden Görevler görünümüyle birlikte yararlı olabilir.

  • Sanal ç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 sanal çağrı yığınlarına nasıl uygulandığını gösterir. Sanal çağrı yığınının yalnızca özdeş kesimleri gruplandırılır. Görevleri çalıştıran iş parçacıklarını belirlemek için gruplandırılmış çağrı yığınının üzerine gelin.

    Sanal çağrı yığınlarının gruplandırma çizimi.

C# örneği

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ı, zaman uyumsuz bir uygulamada hata ayıklamak için Paralel Yığınlar penceresinin Görevler görünümünün nasıl kullanılacağını anlamaktır.

Örnek, iş parçacığı havuzu açlığıyla sonuçlanabilecek senkron üzerinden asenkron antipattern kullanımına bir örnek içerir.

Çağrı yığınını sezgisel hale getirmek için örnek uygulama aşağıdaki sıralı adımları gerçekleştirir:

  1. Gorilleri temsil eden bir nesne oluşturur.
  2. Goril uyanır.
  3. Goril sabah yürüyüşüne çıkıyor.
  4. Goril ormanda muz bulur.
  5. Goril yiyor.
  6. Goril maymun işine girer.

Örnek projeyi oluşturma

  1. Visual Studio'yu açın ve yeni bir proje oluşturun.

    Başlangıç penceresi açık değilse DosyaBaşlangıç Penceresiseç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# seçin ve ardından Platform listesinden windows seçin.

    Dil ve platform filtrelerini uyguladıktan sonra .NET için Konsol Uygulaması'nı ve ardından İleri'yi seçin.

    Uyarı

    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 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.

  2. Projede .cs kod dosyasını açın. Boş bir kod dosyası oluşturmak için içeriğini silin.

  3. Seçtiğiniz dil için aşağıdaki kodu boş kod dosyasına yapıştırın.

    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");
             }
         }
    }
    

    Kod dosyasını güncelleştirdikten sonra değişikliklerinizi kaydedin ve çözümü oluşturun.

  4. Dosya menüsünde Tümünü Kaydet'i seçin.

  5. Derle menüsünde Çözüm Derle'yi seçin.

Paralel Yığınlar penceresinde Görevler Görünümünü kullanın

  1. Hata Ayıkla menüsünde Hata Ayıklamayı Başlat 'ı (veya F5) seçin ve ilkin Debugger.Break() isabetini bekleyin.

  2. F5 tuşuna bir kez bastığınızda hata ayıklayıcı aynı Debugger.Break() satırda yeniden duraklatılır.

    Bu, ikinci bir zaman uyumsuz görev içinde gerçekleşen Gorilla_Start'nin ikinci çağrısında duraklar.

  3. 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 Görevler'i seçin.

    Paralel Yığınlar penceresinde Görevler görünümünün ekran görüntüsü.

    Zaman uyumsuz çağrı yığınlarının etiketlerinin 2 Zaman Uyumsuz Mantıksal Yığını açıkladığına dikkat edin. F5'e en son bastığınızda başka bir görev başlattınız. Karmaşık uygulamaları basitleştirmek amacıyla, özdeş asenkron çağrı yığınları tek bir görsel temsil içinde bir araya getirilir. Bu, özellikle birçok görevi olan senaryolarda daha eksiksiz bilgiler sağlar.

    Görevler görünümünün aksine, Çağrı Yığını penceresi yalnızca geçerli iş parçacığının çağrı yığınını gösterir, birden çok görev için değil. Uygulama durumunun daha eksiksiz bir resmi için her ikisini birlikte görüntülemek genellikle yararlı olur.

    Call Stack ekran görüntüsü.

    Tavsiye

    Çağrı Yığını penceresi, açıklamasını Async cyclekullanarak kilitlenme gibi bilgileri gösterebilir.

    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 Çağrı Yığını penceresinin Ad tablosu üst bilgisine sağ tıklayın ve Dış Kodu Göster seçeneğini seçin veya kaldırın. Dış kod gösterirseniz, bu kılavuzu kullanmaya devam edebilirsiniz, ancak sonuçlarınız çizimlerden farklı olabilir.

  4. F5 tuşuna yeniden basın, hata ayıklayıcı DoSomeMonkeyBusiness yönteminde durur.

    F5'in ardından Görevler görünümünün ekran görüntüsü.

    Bu görünüm, await ve benzeri yöntemler kullanılırken iç devamlılık zincirine daha fazla zaman uyumsuz yöntem eklendikten sonra, daha eksiksiz bir zaman uyumsuz çağrı yığını gösterir. DoSomeMonkeyBusiness zaman uyumsuz bir yöntem olduğundan ancak devam zincirine henüz eklenmediği için zaman uyumsuz çağrı yığınının en üstünde bulunabilir veya olmayabilir. İzleyen adımlarda bunun neden böyle olduğunu keşfedeceğiz.

    Bu görünümde Jungle.MainDurum Engellendi simgesi de gösterilir. Bu bilgilendiricidir, ancak genellikle bir sorunu göstermez. Diğer bir görevin bitmesini bekleyen, bir olayın tetiklenmesini ya da kilidin serbest bırakılmasını beklediği için engellenen görev, engellenmiş bir görevdir.

  5. GobbleUpBananas yönteminin üzerine gelin ve görevleri çalıştıran iki iş parçacığı hakkında bilgi edinin.

    Çağrı yığınıyla ilişkili thread'lerin ekran görüntüsü.

    Geçerli iş parçacığı, Debug araç çubuğundaki İş Parçacığı listesinde de görünür.

    Debug araç çubuğundaki geçerli iş parçacığının ekran görüntüsü.

    Hata ayıklayıcısı bağlamını farklı bir iş parçacığına değiştirmek için İş Parçacığı listesini kullanabilirsiniz.

  6. F5 tuşuna yeniden basın ve hata ayıklayıcı ikinci görevin metotunda durur.

    İkinci F5'in ardından Görevler görünümünün ekran görüntüsü.

    Görevin yürütülme zamanına bağlı olarak, bu noktada ya ayrı ya da gruplandırılmış eşzamanlı olmayan çağrı yığınlarını görürsünüz.

    Yukarıdaki çizimde, iki görev için zaman uyumsuz çağrı yığınları ayrıdır çünkü bunlar özdeş değildir.

  7. F5 tuşuna yeniden bastığınızda uzun bir gecikme olduğunu görürsünüz ve Görevler görünümünde zaman uyumsuz çağrı yığını bilgisi gösterilmez.

    Gecikme, uzun süre çalışan bir görevden kaynaklanır. Bu örnek amacıyla, bir web isteği gibi uzun süre çalışan bir görevin simülasyonunu gerçekleştirir; bu da iş parçacığı havuzu yoksunluğuna neden olabilir. Görevler engellenmiş olsa bile hata ayıklayıcıda şu an duraklatılmadığınız için Görevler görünümünde hiçbir şey görünmez.

    Tavsiye

    Kilitlenme oluşursa veya tüm görevler ve iş parçacıkları şu anda engellenirse Tümünü Kes düğmesi çağrı yığını bilgilerini almak için iyi bir yoldur.

  8. Hata Ayıklama araç çubuğundaki IDE'nin üst kısmında Tümünü Kes düğmesini (duraklat simgesi), Ctrl + Alt + Break'i seçin.

    Tümünü Kes'i seçtikten sonra Görevler görünümünün ekran görüntüsü.

    Görevler görünümünde, zaman uyumsuz çağrı yığınının üst kısmında GobbleUpBananas engellenmiş olduğunu görürsünüz. Aslında iki görev aynı noktada engellenir. Engellenen görev beklenmedik olmayabilir ve bir sorun olduğu anlamına gelmez. Ancak, yürütmede gözlemlenen gecikme bir sorunu gösterir ve buradaki çağrı yığını bilgileri sorunun konumunu gösterir.

    Önceki ekran görüntüsünün sol tarafında, kıvrılmış yeşil ok geçerli hata ayıklayıcı bağlamını gösterir. mb.Wait()'da iki görev GobbleUpBananas yönteminde engellenir.

    Çağrı Yığını penceresi, geçerli iş parçacığının engellendiğini de gösterir.

    Tümünü Kes'i seçtikten sonra Çağrı Yığını'nın ekran görüntüsü.

    çağrısı, Wait() zaman uyumlu çağrısı içindeki iş parçacıklarını GobbleUpBananasengeller. Bu, senkron-üzerinden-asenkron anti-deseninin bir örneğidir; eğer bu durum bir UI iş parçacığında veya büyük işleme iş yükleri altında meydana gelirse, genellikle await kullanılarak bir kod düzeltmesi ile giderilir. Daha fazla bilgi için bkz. İş parçacığı havuzu açlığını giderme. İş parçacığı havuzu yetersizliğinde hata ayıklamak için profil oluşturma araçlarını kullanmayı öğrenmek amacıyla bkz Vaka incelemesi: Performans sorununu yalıtma.

    Ayrıca ilginç olan, DoSomeMonkeyBusiness çağrı yığınında görünmez. Şu anda çalışmıyor, yalnızca zamanlanmıştır, bu nedenle yalnızca Görevler görünümündeki eşzamansız çağrı yığınında görünür.

    Tavsiye

    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 bunu yönetmeniz gerekiyorsa, ek kesme noktaları ekleyebilir, koşullu kesme noktaları ekleyebilir veya Tümünü Kes'i kullanabilirsiniz. Bu davranış hakkında daha fazla bilgi için bkz. Koşullu kesme noktalarıyla tek bir iş parçacığını izleme.

Örnek kodu düzeltme

  1. GobbleUpBananas yöntemini aşağıdaki kodla değiştirin.

     public async Task GobbleUpBananas(int food) // Previously returned void.
     {
         Console.WriteLine("Trying to gobble up food...");
    
         //Task mb = DoSomeMonkeyBusiness();
         //mb.Wait();
         await DoSomeMonkeyBusiness();
     }
    
  2. yönteminde MorningWalk kullanarak awaitGobbleUpBananas'ı çağırın.

    await GobbleUpBananas(myResult);
    
  3. Yeniden Başlat düğmesini (Ctrl + Shift + F5) seçin ve ardından uygulama "askıda" görünene kadar F5 tuşuna birkaç kez basın.

  4. Hepsini Sonlandır'a basın.

    Bu sefer GobbleUpBananas uyumsuz olarak çalışır. Kod durduğunda, asenkron çağrı yığınını görürsünüz.

    Kod düzeltmesinin ardından hata ayıklayıcısı bağlamının ekran görüntüsü.

    Çağrı Yığını penceresi ExternalCode girişi dışında boştur.

    Kod düzenleyicisi bize hiçbir şey göstermez, ancak tüm iş parçacıklarının dış kodu yürüttüğüne dair bir ileti sağlar.

    Ancak Görevler görünümü yararlı bilgiler sağlar. DoSomeMonkeyBusiness beklendiği gibi asenkron çağrı yığınının en üstündedir. Bu, uzun süre çalışan yöntemin nerede bulunduğunu doğru bir şekilde bildirir. Bu, çağrı yığını penceresinde yer alan fiziksel çağrı yığını yeterli ayrıntı sağlamadığında, async/await sorunlarını izole etmekte faydalıdır.

Özet

Bu kılavuzda Parallel Stacks hata ayıklayıcısı penceresi gösterilmiştir. Asenkron/await desenini kullanan uygulamalarda bu pencereyi kullanın.