Megosztás a következőn keresztül:


Aszinkron alkalmazás hibakeresése

Ez az oktatóanyag bemutatja, hogyan használhatja a Párhuzamos halmok ablak Feladatok nézetét egy C#-aszinkron alkalmazás hibakereséséhez. Ez az ablak segít megérteni és ellenőrizni az aszinkron/várakozási mintát (más néven feladatalapú aszinkron mintát (TAP) használó kód futásidejű viselkedését.

A párhuzamos feladattárat (TPL) használó, de az aszinkron/várakozási mintát nem használó alkalmazások, illetve az egyidejűségi futtatókörnyezetet használó C++ alkalmazások esetében a Párhuzamos verem ablakban a Szálak nézetet használhatja a hibakereséshez. További információ: Holtpont hibakeresése és Szálak és feladatok megtekintése a Párhuzamos verem ablakban.

A Feladatok nézet a következőkhöz nyújt segítséget:

  • Az aszinkron/várakozási mintát használó alkalmazások hívásveremvizualizációinak megtekintése. Ezekben a forgatókönyvekben a Feladatok nézet teljesebb képet nyújt az alkalmazás állapotáról.

  • Azonosítsa a futtatásra ütemezett, de még nem futó aszinkron kódot. Például egy olyan HTTP-kérés, amely nem adott vissza adatokat, valószínűbb, hogy a Szálak nézet helyett a Feladatok nézetben jelenik meg, ami segít elkülöníteni a problémát.

  • Segít azonosítani az olyan problémákat, mint a szinkronizálási aszinkron minta, valamint a lehetséges problémákra, például a blokkolt vagy várakozó feladatokra vonatkozó tippeket. A sync-over-async kódminta olyan kódra utal, amely szinkron módon hív aszinkron metódusokat. Ez ismert módon blokkolja a szálakat, és ez a szálkészlet éhezésének leggyakoribb oka.

Aszinkron hívásveremek

A Párhuzamos halmok Feladatok nézete vizualizációt biztosít az aszinkron hívásveremekhez, így láthatja, hogy mi történik (vagy kellene) az alkalmazásban.

Az alábbiakban néhány fontos szempontot érdemes megjegyezni, amikor a Feladatok nézetben értelmezi az adatokat.

  • Az aszinkron hívásveremek logikai vagy virtuális hívásveremek, nem pedig a vermet képviselő fizikai hívásveremek. Ha aszinkron kóddal dolgozik (például a await kulcsszó használatával), a hibakereső megjeleníti az "aszinkron hívásveremek" vagy a "virtuális hívásveremek" nézetét. Az aszinkron hívásveremek eltérnek a szálalapú hívásveremektől vagy a "fizikai veremektől", mivel az aszinkron hívásveremek jelenleg nem feltétlenül futnak fizikai szálon. Ehelyett az aszinkron hívásveremek olyan kód folytatásai vagy "ígéretei", amelyek a jövőben aszinkron módon fognak futni. A hívásveremek folytatások használatával jönnek létre.

  • Az ütemezett, de jelenleg nem futó aszinkron kód nem jelenik meg a fizikai hívásveremen, de a Feladatok nézetben az aszinkron hívásveremen kell megjelennie. Ha olyan metódusokkal blokkolja a szálakat, mint a .Wait vagy .Result, akkor a kód a fizikai hívásveremben jelenhet meg.

  • Az aszinkron virtuális hívásveremek nem mindig intuitívak, mivel az elágazás olyan metódushívások használatából ered, mint például .WaitAny vagy .WaitAll.

  • A Hívásverem ablak a Feladatok nézettel kombinálva hasznos lehet, mivel az aktuális végrehajtási szálhoz tartozó fizikai hívásverem látható.

  • A virtuális hívásverem azonos szakaszai csoportosítva vannak az összetett alkalmazások vizualizációjának egyszerűsítése érdekében.

    Az alábbi fogalmi animáció bemutatja, hogyan alkalmazzák a virtuális hívási veremeken a csoportosítást. Csak a virtuális hívásverem azonos szegmensei vannak csoportosítva. Vigye az egérmutatót egy csoportosított hívásveremre, hogy azonosítsa a feladatokat futtató szálakat.

    A virtuális hívásveremek csoportosításának ábrája.

C#-minta

Az útmutató mintakódja egy olyan alkalmazáshoz készült, amely egy gorilla életének egy napját szimulálja. A gyakorlat célja annak megismerése, hogyan használható a Párhuzamos halmok ablak Feladatok nézete egy aszinkron alkalmazás hibakereséséhez.

A minta egy példát tartalmaz a sync-over-async mintázat hibás alkalmazásának bemutatására, ami a szálkészlet kimerüléséhez vezethet.

A hívásverem intuitívsá tétele érdekében a mintaalkalmazás a következő szekvenciális lépéseket hajtja végre:

  1. Létrehoz egy gorillát jelképező objektumot.
  2. Gorilla felébred.
  3. Gorilla megy egy reggeli sétára.
  4. Gorilla banánt talál a dzsungelben.
  5. Gorilla eszik.
  6. A gorilla bohóckodik.

A mintaprojekt létrehozása

  1. Nyissa meg a Visual Studiót, és hozzon létre egy új projektet.

    Ha a kezdőablak nincs megnyitva, válassza Fájl>Ablak indításalehetőséget.

    A kezdőablakban válassza az Új projekt lehetőséget.

    Az Új projekt létrehozása ablakban írja be vagy írja be a keresőmezőbe a konzolt . Ezután válassza C# a Nyelv listából, majd válassza Windows a Platform listából.

    A nyelv- és platformszűrők alkalmazása után válassza a .NET konzolalkalmazást , majd a Tovább gombot.

    Megjegyzés:

    Ha nem látja a megfelelő sablont, lépjen az Eszközök>lekérése eszközök és szolgáltatások lapra, amely megnyitja a Visual Studio Installert. Válassza a .NET asztali fejlesztés munka terhelését, majd válassza a Módosítás lehetőséget.

    Az új projekt konfigurálása ablakban írjon be egy nevet, vagy használja az alapértelmezett nevet a Projektnév mezőbe. Ezután válassza a Következőlehetőséget.

    .NET esetén válassza az ajánlott cél keretrendszert vagy a .NET 8-at, majd válassza a Létrehozás lehetőséget.

    Megjelenik egy új konzolprojekt. A projekt létrehozása után megjelenik egy forrásfájl.

  2. Nyissa meg a .cs kódfájlt a projektben. Üres kódfájl létrehozásához törölje annak tartalmát.

  3. Illessze be a választott nyelvhez tartozó alábbi kódot az üres kódfájlba.

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

    A kódfájl frissítése után mentse a módosításokat, és hozza létre a megoldást.

  4. A Fájl menüben válassza az Összes mentése lehetőséget.

  5. A Build menüben válassza a Megoldás összeállításalehetőséget.

A Párhuzamos halmok ablak Feladatok nézetének használata

  1. A Hibakeresés menüben válassza a Hibakeresés indítása (vagy F5) lehetőséget, és várja meg az első Debugger.Break() találatot.

  2. Nyomja le egyszer az F5 billentyűt, és a hibakereső ismét megáll ugyanazon Debugger.Break() a sorban.

    Ez szünetel a második hívásban Gorilla_Start, amely egy második aszinkron feladaton belül történik.

  3. A Párhuzamos halmok ablak megnyitásához válassza a Windows > párhuzamos halmok hibakeresése > lehetőséget, majd az ablak Nézet legördülő listájában válassza a Feladatok lehetőséget.

    Képernyőkép a Feladatok nézetről a Párhuzamos halmok ablakban.

    Figyelje meg, hogy az aszinkron hívásverem címkéi 2 aszinkron logikai vermet írnak le. Amikor legutóbb lenyomta az F5 billentyűt, egy másik feladatot indított el. Az összetett alkalmazások egyszerűsítése érdekében az azonos aszinkron hívásveremek egyetlen vizuális ábrázolásba vannak csoportosítva. Ez teljesebb információkat nyújt, különösen a sok feladatot tartalmazó forgatókönyvekben.

    A Feladatok nézettel ellentétben a Hívásverem ablak csak az aktuális szál hívásveremét jeleníti meg, több tevékenység esetén nem. Gyakran hasznos, ha mindkettőt együtt tekinti meg, hogy teljesebb képet adjon az alkalmazás állapotáról.

    Képernyőkép a hívásveremről.

    Jótanács

    A Hívásverem ablak megjelenítheti az olyan információkat, mint például a holtpont, a leírás Async cycle alapján.

    A hibakeresés során beállíthatja, hogy megjelenjen-e külső kód. A funkció váltásához kattintson a jobb gombbal a Hívásverem ablak Névtábla fejlécére, majd válassza a Külső kód megjelenítése lehetőséget, vagy törölje a jelölését. Ha külső kódot jelenít meg, akkor is használhatja ezt az útmutatót, de az eredmények eltérhetnek az ábráktól.

  4. Nyomja le ismét az F5 billentyűt, és a hibakereső szünetelteti a DoSomeMonkeyBusiness metódust.

    Képernyőkép a Feladatok nézetről az F5 után.

    Ez a nézet teljesebb aszinkron hívásvermet mutat, miután több aszinkron metódust hozzáadtak a belső folytatási láncolathoz, ami az olyan metódusok használatakor fordul elő, mint a await. DoSomeMonkeyBusiness lehet, hogy jelen van vagy nincs az aszinkron hívásverem tetején, mivel egy aszinkron metódusról van szó, amely még nincs hozzáadva a végrehajtási lánchoz. A következő lépésekben megvizsgáljuk, hogy miért ez a helyzet.

    Ebben a nézetben a Jungle.Main letiltott ikonja Status Blocked is látható. Ez informatív, de általában nem jelez problémát. A blokkolt tevékenység azért van letiltva, mert egy másik tevékenységre vár, egy jelzésre váró eseményre vagy egy feloldandó zárolásra.

  5. Vigye az egérmutatót a GobbleUpBananas metódusra, hogy információkat kapjon a feladatokat futtató két szálról.

    Képernyőkép a hívásveremhez társított szálakról.

    Az aktuális szál a Hibakeresés eszköztár Szál listájában is megjelenik.

    Képernyőkép a hibakeresési eszköztár aktuális száláról.

    A szál lista használatával a hibakereső környezetet egy másik szálra válthatja.

  6. Nyomja le ismét az F5 billentyűt, és a hibakereső szünetelteti a DoSomeMonkeyBusiness második feladat metódusát.

    Képernyőkép a Feladatok nézetről a második F5 után.

    A feladat végrehajtásának időzítésétől függően ezen a ponton különálló vagy csoportosított aszinkron hívásveremek jelennek meg.

    Az előző ábrán a két tevékenység aszinkron hívásveremei külön-külön vannak, mert nem azonosak.

  7. Nyomja le ismét az F5 billentyűt, és hosszú késés jelenik meg, és a Feladatok nézet nem jelenít meg aszinkron hívásveremadatokat.

    A késést egy hosszan futó feladat okozza. Ebben a példában egy hosszú ideig futó feladatot szimulál, például egy webes kérést, amely a szálkészlet éhezését eredményezheti. A Feladatok nézetben semmi sem jelenik meg, mert bár a feladatok le lehetnek tiltva, Ön jelenleg nincs szüneteltetve a hibakeresőben.

    Jótanács

    Az Összes megszakítás gomb jó módszer a hívásverem információinak lekérésére, ha holtpont áll fenn, vagy az összes tevékenység és szál jelenleg le van tiltva.

  8. A hibakeresési eszköztár IDE tetején válassza az Összes megszakítása gombot (szüneteltetés ikon), Ctrl+ Alt+ Break.

    Képernyőkép a Feladatok nézetről az Összes megszakítása lehetőség kiválasztása után.

    A Feladatok nézetben az aszinkron hívásverem tetején látható, hogy GobbleUpBananas ez le van tiltva. Valójában két tevékenység blokkolva van ugyanazon a ponton. A blokkolt tevékenység nem feltétlenül váratlan, és nem feltétlenül jelenti azt, hogy probléma merült fel. A végrehajtás megfigyelt késése azonban problémát jelez, és az itt található hívásverem-információk a probléma helyét mutatják.

    Az előző képernyőkép bal oldalán a görbült zöld nyíl jelzi az aktuális hibakereső környezetet. A két feladat blokkolva van mb.Wait() a GobbleUpBananas metódusban.

    A Hívásverem ablak azt is megjeleníti, hogy az aktuális szál blokkolva van.

    Képernyőkép a hívásveremről, miután kiválasztotta a Mindent megszakít lehetőséget.

    A Wait() hívása letiltja a szálakat a GobbleUpBananas-hez való szinkron híváson belül. Ez egy példa a sync-over-async antipatternre, és ha ez egy felhasználói felületen vagy nagy feldolgozási munkaterhelés alatt történt, akkor általában egy kódjavítással, await oldják meg a problémát. További információért lásd: A szálkészlet kimerülésének hibakeresése. Ha profilkészítési eszközöket használ a szálkészlet éhezésének hibakeresésére, tekintse meg az Esettanulmány: Teljesítményproblémák elkülönítése részt.

    Szintén érdekes, DoSomeMonkeyBusiness nem jelenik meg a hívási veremben. Jelenleg ütemezett, nem fut, ezért csak az aszinkron hívásveremben jelenik meg a Feladatok nézetben.

    Jótanács

    A hibakereső szálonként törik át a kódot. Ez azt jelenti például, hogy ha az F5 billentyűt lenyomva folytatja a végrehajtást, és az alkalmazás eléri a következő töréspontot, az egy másik szálon lévő kódra törhet. Ha hibakeresési célból kell kezelnie ezt a funkciót, hozzáadhat további töréspontokat, hozzáadhat feltételes töréspontokat, vagy használhatja az Összes megszakítást. További információ erről a viselkedésről: Egyetlen szál követése feltételes töréspontokkal.

A mintakód javítása

  1. Cserélje le a metódust GobbleUpBananas a következő kódra.

     public async Task GobbleUpBananas(int food) // Previously returned void.
     {
         Console.WriteLine("Trying to gobble up food...");
    
         //Task mb = DoSomeMonkeyBusiness();
         //mb.Wait();
         await DoSomeMonkeyBusiness();
     }
    
  2. A metódusban a GobbleUpBananas függvényt hívja meg MorningWalk használatával await.

    await GobbleUpBananas(myResult);
    
  3. Válassza az Újraindítás gombot (Ctrl + Shift + F5), majd nyomja le többször az F5 billentyűt, amíg az alkalmazás le nem áll.

  4. Nyomja meg a 'Mindent megszakít' gombot.

    Ezúttal GobbleUpBananas aszinkron módon fut. Ha megszakad, megjelenik az aszinkron hívásverem.

    Képernyőkép a hibakereső környezetéről a kódjavítás után.

    A Hívásverem ablak üres, kivéve a bejegyzést ExternalCode .

    A kódszerkesztő nem jelenít meg semmit, csak azt jelzi, hogy az összes szál külső kódot hajt végre.

    A Feladatok nézet azonban hasznos információkat nyújt. DoSomeMonkeyBusiness a várt módon az aszinkron hívásverem tetején található. Ez helyesen jelzi, hogy hol található a hosszú futású metódus. Ez hasznos az aszinkron/várakozási problémák elkülönítéséhez, ha a Hívásverem ablakban lévő fizikai hívásverem nem nyújt elegendő adatot.

Összefoglalás

Ez az útmutató a Párhuzamos verem hibakereső ablakát mutatta be. Használja ezt az ablakot az aszinkron/várakozási mintát használó alkalmazásokban.