Procédure pas à pas : débogage d’une application parallèle dans Visual Studio (C#, Visual Basic, C++)

Cette procédure pas à pas indique comment utiliser les fenêtres Tâches parallèles et Piles parallèles pour déboguer une application parallèle. Ces fenêtres permettent de comprendre et de vérifier le comportement du code à l’exécution qui utilise la bibliothèque parallèle de tâches (TPL) ou le runtime d’accès concurrentiel. Cette procédure pas à pas fournit un exemple de code qui comporte des points d'arrêt intégrés. Une fois le code arrêté, la procédure pas à pas indique comment utiliser les fenêtres Tâches parallèles et Piles parallèles pour l’examiner.

Cette procédure pas à pas aborde les tâches suivantes :

  • Comment afficher les piles d'appels de tous les threads dans une vue.

  • Comment afficher la liste des instances System.Threading.Tasks.Task créées dans votre application.

  • Comment afficher les véritables piles d’appels des tâches au lieu des threads.

  • Comment accéder au code à partir des fenêtres Tâches parallèles et Piles parallèles.

  • Comment les fenêtres gèrent l'échelle avec les fonctionnalités de regroupement, de zoom et autres.

Prérequis

Cette procédure pas à pas suppose que l’option Uniquement mon code est activée (activée par défaut dans les versions plus récentes de Visual Studio). Dans le menu Outils, sélectionnez Options, développez le nœud Débogage, cliquez sur Général, puis Activer Uniquement mon code (Managé uniquement). Si vous ne définissez pas cette fonctionnalité, vous pouvez quand même effectuer cette procédure pas à pas, mais vos résultats pourraient différer de ceux des illustrations.

Exemple de code C#

Si vous utilisez l'exemple C#, cette procédure pas à pas suppose également que le code externe est masqué. Pour afficher ou masquer le code externe, cliquez avec le bouton droit sur l’en-tête de table Nom de la fenêtre Pile des appels, puis activez ou désactivez Afficher le code externe. Si vous ne définissez pas cette fonctionnalité, vous pouvez quand même effectuer cette procédure pas à pas, mais vos résultats pourraient différer de ceux des illustrations.

Exemple C++

Si vous utilisez l'exemple C++, vous pouvez ignorer les références au code externe de cet article. Le code externe s'applique uniquement à l'exemple C#.

Illustrations

Les illustrations de cet article ont été enregistrées sur un ordinateur quadruple cœur exécutant l'exemple C#. Bien que vous puissiez utiliser d'autres configurations pour effectuer cette procédure pas à pas, les illustrations pourrait différer de celles affichées sur votre ordinateur.

Créer l’exemple de projet

L'exemple de code de cette procédure pas à pas est relatif à une application qui ne fait rien. L'objectif de l'exercice est de comprendre comment utiliser les fenêtres Outils pour déboguer une application parallèle.

  1. Ouvrez Visual Studio et créez un projet.

    Si la fenêtre de démarrage n’est pas ouverte, choisissez Fichier>Fenêtre Démarrer.

    Dans la fenêtre de démarrage, choisissez Nouveau projet.

    Dans la fenêtre de démarrage, choisissez Créer un projet.

    Dans la fenêtre Créer un projet, entrez ou tapez console dans la zone de recherche. Choisissez ensuite C#, C++ ou Visual Basic dans la liste des langages, puis choisissez Windows dans la liste des plateformes.

    Après avoir appliqué les filtres de langage et de plateforme, choisissez Application console pour .NET Core ou C++, puis choisissez Suivant.

    Notes

    Si vous ne voyez pas le bon modèle, accédez à Outils>Obtenir des outils et fonctionnalités… pour ouvrir Visual Studio Installer. Choisissez la charge de travail Développement .NET Desktop ou Développement Desktop avec C++, puis choisissez Modifier.

    Dans la fenêtre Configurer votre nouveau projet, tapez un nom ou utilisez le nom par défaut dans la zone Nom du projet. Choisissez ensuite Suivant ou Créer, selon l’option disponible.

    Pour .NET Core, choisissez le framework cible recommandé ou .NET 8, puis choisissez Créer.

    Un nouveau projet console s'affiche. Une fois le projet créé, un fichier source s’affiche.

  2. Ouvrez le fichier de code .cpp, .cs ou .vb dans le projet. Supprimez son contenu pour créer un fichier de code vide.

  3. Collez le code suivant dans le langage choisi dans le fichier de code vide.

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Diagnostics;
    
    class S
    {
      static void Main()
      {
        pcount = Environment.ProcessorCount;
        Console.WriteLine("Proc count = " + pcount);
        ThreadPool.SetMinThreads(4, -1);
        ThreadPool.SetMaxThreads(4, -1);
    
        t1 = new Task(A, 1);
        t2 = new Task(A, 2);
        t3 = new Task(A, 3);
        t4 = new Task(A, 4);
        Console.WriteLine("Starting t1 " + t1.Id.ToString());
        t1.Start();
        Console.WriteLine("Starting t2 " + t2.Id.ToString());
        t2.Start();
        Console.WriteLine("Starting t3 " + t3.Id.ToString());
        t3.Start();
        Console.WriteLine("Starting t4 " + t4.Id.ToString());
        t4.Start();
    
        Console.ReadLine();
      }
    
      static void A(object o)
      {
        B(o);
      }
      static void B(object o)
      {
        C(o);
      }
      static void C(object o)
      {
        int temp = (int)o;
    
        Interlocked.Increment(ref aa);
        while (aa < 4)
        {
          ;
        }
    
        if (temp == 1)
        {
          // BP1 - all tasks in C
          Debugger.Break();
          waitFor1 = false;
        }
        else
        {
          while (waitFor1)
          {
            ;
          }
        }
        switch (temp)
        {
          case 1:
            D(o);
            break;
          case 2:
            F(o);
            break;
          case 3:
          case 4:
            I(o);
            break;
          default:
            Debug.Assert(false, "fool");
            break;
        }
      }
      static void D(object o)
      {
        E(o);
      }
      static void E(object o)
      {
        // break here at the same time as H and K
        while (bb < 2)
        {
          ;
        }
        //BP2 - 1 in E, 2 in H, 3 in J, 4 in K
        Debugger.Break();
        Interlocked.Increment(ref bb);
    
        //after
        L(o);
      }
      static void F(object o)
      {
        G(o);
      }
      static void G(object o)
      {
        H(o);
      }
      static void H(object o)
      {
        // break here at the same time as E and K
        Interlocked.Increment(ref bb);
        Monitor.Enter(mylock);
        while (bb < 3)
        {
          ;
        }
        Monitor.Exit(mylock);
    
    
        //after
        L(o);
      }
      static void I(object o)
      {
        J(o);
      }
      static void J(object o)
      {
        int temp2 = (int)o;
    
        switch (temp2)
        {
          case 3:
            t4.Wait();
            break;
          case 4:
            K(o);
            break;
          default:
            Debug.Assert(false, "fool2");
            break;
        }
      }
      static void K(object o)
      {
        // break here at the same time as E and H
        Interlocked.Increment(ref bb);
        Monitor.Enter(mylock);
        while (bb < 3)
        {
          ;
        }
        Monitor.Exit(mylock);
    
    
        //after
        L(o);
      }
      static void L(object oo)
      {
        int temp3 = (int)oo;
    
        switch (temp3)
        {
          case 1:
            M(oo);
            break;
          case 2:
            N(oo);
            break;
          case 4:
            O(oo);
            break;
          default:
            Debug.Assert(false, "fool3");
            break;
        }
      }
      static void M(object o)
      {
        // breaks here at the same time as N and Q
        Interlocked.Increment(ref cc);
        while (cc < 3)
        {
          ;
        }
        //BP3 - 1 in M, 2 in N, 3 still in J, 4 in O, 5 in Q
        Debugger.Break();
        Interlocked.Increment(ref cc);
        while (true)
          Thread.Sleep(500); // for ever
      }
      static void N(object o)
      {
        // breaks here at the same time as M and Q
        Interlocked.Increment(ref cc);
        while (cc < 4)
        {
          ;
        }
        R(o);
      }
      static void O(object o)
      {
        Task t5 = Task.Factory.StartNew(P, TaskCreationOptions.AttachedToParent);
        t5.Wait();
        R(o);
      }
      static void P()
      {
        Console.WriteLine("t5 runs " + Task.CurrentId.ToString());
        Q();
      }
      static void Q()
      {
        // breaks here at the same time as N and M
        Interlocked.Increment(ref cc);
        while (cc < 4)
        {
          ;
        }
        // task 5 dies here freeing task 4 (its parent)
        Console.WriteLine("t5 dies " + Task.CurrentId.ToString());
        waitFor5 = false;
      }
      static void R(object o)
      {
        if ((int)o == 2)
        {
          //wait for task5 to die
          while (waitFor5) { ;}
    
    
          int i;
          //spin up all procs
          for (i = 0; i < pcount - 4; i++)
          {
            Task t = Task.Factory.StartNew(() => { while (true);});
            Console.WriteLine("Started task " + t.Id.ToString());
          }
    
          Task.Factory.StartNew(T, i + 1 + 5, TaskCreationOptions.AttachedToParent); //scheduled
          Task.Factory.StartNew(T, i + 2 + 5, TaskCreationOptions.AttachedToParent); //scheduled
          Task.Factory.StartNew(T, i + 3 + 5, TaskCreationOptions.AttachedToParent); //scheduled
          Task.Factory.StartNew(T, i + 4 + 5, TaskCreationOptions.AttachedToParent); //scheduled
          Task.Factory.StartNew(T, (i + 5 + 5).ToString(), TaskCreationOptions.AttachedToParent); //scheduled
    
          //BP4 - 1 in M, 2 in R, 3 in J, 4 in R, 5 died
          Debugger.Break();
        }
        else
        {
          Debug.Assert((int)o == 4);
          t3.Wait();
        }
      }
      static void T(object o)
      {
        Console.WriteLine("Scheduled run " + Task.CurrentId.ToString());
      }
      static Task t1, t2, t3, t4;
      static int aa = 0;
      static int bb = 0;
      static int cc = 0;
      static bool waitFor1 = true;
      static bool waitFor5 = true;
      static int pcount;
      static S mylock = new S();
    }
    

Après avoir mis à jour le fichier de code, enregistrez vos modifications et générez la solution.

  1. Dans le menu Fichier, sélectionnez Enregistrer tout.

  2. Dans le menu Générer, sélectionnez Régénérer la solution.

Notez qu’il existe quatre appels à Debugger.Break (DebugBreak dans l’exemple C++). Par conséquent, vous n’avez pas besoin d’insérer de points d’arrêt. L’exécution de l’application entraîne son arrêt dans le débogueur jusqu’à quatre fois.

Utiliser la fenêtre Piles parallèles : vue Threads

Pour commencer, dans le menu Déboguer, sélectionnez Lancer le débogage. Attendez que le premier point d'arrêt soit atteint.

Affichez la pile des appels d'un thread unique

  1. Dans le menu Déboguer, pointez sur Fenêtres, puis sélectionnez Threads. Ancrez la fenêtre Threads en bas de Visual Studio.

  2. Dans le menu Déboguer, pointez sur Fenêtres, puis sélectionnez Pile des appels. Ancrez la fenêtre Pile des appels en bas de Visual Studio.

  3. Double-cliquez sur un thread dans la fenêtre Threads pour le rendre actuel. Les threads actuels comportent une flèche jaune. Quand vous modifiez le thread actuel, sa pile des appels est affichée dans la fenêtre Pile des appels.

Examinez la fenêtre Piles parallèles

Dans le menu Déboguer, pointez sur Fenêtres, puis sélectionne Piles parallèles. Assurez-vous que l’option Threads est sélectionnée dans la zone située dans l’angle supérieur gauche.

Grâce à la fenêtre Piles parallèles, vous pouvez afficher plusieurs piles d’appels simultanément au sein d’une vue unique. L’illustration suivante montre la fenêtre Piles parallèles au-dessus de la fenêtre Pile des appels.

Screenshot of Threads view in Parallel Stacks window.

Threads view in Parallel Stacks window

La pile des appels du thread principal s'affiche dans une zone et les piles des appels des quatre autres threads sont regroupées dans une autre zone. Quatre threads sont regroupés car leurs frames de pile partagent les mêmes contextes de méthode, ce qui signifie qu'ils se trouvent dans les mêmes méthodes : A, B et C. Pour afficher les ID et noms de threads qui partagent la même zone, placez le curseur sur l’en-tête avec l’en-tête ([#] Threads). Le thread actuel s’affiche en gras.

Screenshot of Tooltip that shows thread IDs and names.

Tooltip that shows thread IDs and names

La flèche jaune indique le frame de pile actif du thread actuel.

Vous pouvez indiquer le détail souhaité pour les frames de pile (Afficher les noms de modules, Afficher les types de paramètre, Afficher les noms de paramètres, Afficher les valeurs de paramètre, Afficher les numéros de ligne et Afficher les offsets d’octet) en cliquant avec le bouton droit dans la fenêtre Pile des appels.

Une surbrillance bleue autour d'une zone indique que le thread actuel fait partie de cette zone. Le thread actuel est également indiqué par le frame de pile en gras dans l'info-bulle. Si vous double-cliquez sur le thread principal dans la fenêtre Threads, vous constatez que la flèche de mise en évidence de la fenêtre Piles parallèles se déplace en conséquence.

Screenshot of Highlighted main thread in Parallel Stacks window.

Highlighted main thread in Parallel Stacks window

Continuez l'exécution jusqu'au deuxième point d'arrêt

Pour reprendre l’exécution jusqu’au deuxième point d’arrêt, dans le menu Déboguer, sélectionnez Continuer. L'illustration suivante présente l'arborescence des threads au deuxième point d'arrêt.

Screenshot of Parallel Stacks window that shows many branches.

Parallel Stacks window that shows many branches

Au premier point d'arrêt, les quatre threads sont tous allés de la méthode S.A à S.B et S.C. Ces informations sont toujours visibles dans la fenêtre Piles parallèles, mais les quatre threads ont depuis progressé plus loin. L'un d'entre eux a continué vers S.D, puis S.E. Un autre est allé vers S.F, S.G et S.H. Deux autres ont continué vers S.I et S.J et, de là, l'un d'eux est allé vers S.K et l'autre est parti vers du code externe non-utilisateur.

Vous pouvez pointer sur des frames de pile pour afficher les ID des threads et d'autres détails sur les frames. La surbrillance bleue indique le thread actuel et la flèche jaune indique le frame de pile actif du thread actuel.

Vous pouvez pointer sur l’en-tête de zone, par exemple 1 Thread ou 2 Threads, pour afficher les ID des threads. Vous pouvez pointer sur des frames de pile pour afficher les ID des threads et d'autres détails sur les frames. La surbrillance bleue indique le thread actuel et la flèche jaune indique le frame de pile actif du thread actuel.

L’icône de maillage (lignes entrelacées) indique les frames de pile actifs des threads non actuels. Dans la fenêtre Pile des appels, double-cliquez sur S.B pour basculer des frames. La fenêtre Piles parallèles indique le frame de pile actuel du thread actuel à l’aide d’une icône de flèche incurvée.

Notes

Pour obtenir une description de l’ensemble des icônes de la fenêtre Piles parallèles, consultez Utilisation de la fenêtre Piles parallèles.

Dans la fenêtre Threads, basculez entre les threads et remarquez que la vue de la fenêtre Piles parallèles est mise à jour.

Vous pouvez basculer vers un autre thread, ou vers un autre frame d’un autre thread, à l’aide du menu contextuel de la fenêtre Piles parallèles. Par exemple, faites un clic droit sur S.J, pointez sur Basculer vers le frame, puis sélectionnez une commande.

Screenshot of Parallel Stacks Path of Execution.

Parallel Stacks Path of Execution

Cliquez avec le bouton droit sur S.C et pointez sur Basculer vers le frame. L'une des commandes comporte une coche qui indique le frame de pile du thread actuel. Vous pouvez basculer vers ce frame du même thread (seule la flèche incurvée peut se déplacer) ou basculer vers l’autre thread (la mise en évidence bleue se déplace également). L'illustration suivante présente le sous-menu.

Screenshot of Stacks menu with 2 options on C while J is current.

Stacks menu with 2 options on C while J is current

Quand un contexte de méthode est associé à un seul frame de pile, l’en-tête de zone indique 1 Thread et vous pouvez basculer vers lui en double-cliquant. Si vous double-cliquez sur un contexte de méthode associé à plus d'1 frame, le menu apparaît automatiquement. Lorsque vous pointez sur les contextes de méthode, remarquez le triangle noir à droite. Le fait de cliquer sur ce triangle affiche également le menu contextuel.

Pour les applications volumineuses qui comportent de nombreux threads, vous pouvez vous concentrer sur un seul sous-ensemble de threads. La fenêtre Piles parallèles peut afficher des piles d’appels uniquement pour les threads avec indicateur. Pour signaler des threads, utilisez le menu contextuel ou la première cellule d'un thread.

Dans la barre d’outils, sélectionnez le bouton Afficher uniquement les threads avec indicateur situé à côté de la zone de liste.

Screenshot of Parallel Stacks window and tooltip.

Parallel Stacks window and tooltip

Désormais, seuls les threads avec indicateur s’affichent dans la fenêtre Piles parallèles.

Continuez l'exécution jusqu'au troisième point d'arrêt

  1. Pour reprendre l’exécution jusqu’au troisième point d’arrêt, dans le menu Déboguer, sélectionnez Continuer.

    Lorsque plusieurs threads se trouvent dans la même méthode mais que cette méthode ne figure pas au début de la pile des appels, la méthode s'affiche dans des zones différentes. Un exemple au point d'arrêt actuel est S.L, qui possède trois threads et apparaît dans trois zones. Double-cliquez sur S.L.

    Screenshot of Execution path in Parallel Stacks window.

    Execution path in Parallel Stacks window

    Remarquez que S.L apparaît en gras dans les deux autres zones afin que vous puissiez voir où il s'affiche. Si vous souhaitez voir quels frames appeler dans S.L et quels frames sont appelés, sélectionnez le bouton Basculer dans la vue Méthode de la barre d’outils. L’illustration suivante présente la vue Méthode de la fenêtre Piles parallèles.

    Screenshot of Method view in Parallel Stacks window.

    Method view in Parallel Stacks window

    Remarquez comment le diagramme a pivoté sur la méthode sélectionnée et l'a positionnée dans sa propre zone au milieu de la vue. Les appelés et appelants s’affichent en haut et en bas respectivement. Sélectionnez à nouveau le bouton Basculer dans la vue Méthode pour quitter ce mode.

    Le menu contextuel de la fenêtre Piles parallèles comporte également les éléments suivants.

    • Affichage hexadécimal bascule les nombres apparaissant dans les info-bulles entre affichage décimal et hexadécimal.

    • Les paramètres de symbole ouvrent les boîtes de dialogue respectives.

    • Afficher les threads dans source active ou désactive l’affichage des marqueurs de thread dans votre code source, qui indique l’emplacement des threads dans votre code source.

    • Afficher le code externe permet d’afficher tous les frames même s’ils ne figurent pas dans le code utilisateur. Essayez cet élément pour voir le diagramme se développer pour accueillir d’autres frames (qui peuvent être grisés car vous n'avez pas de symboles pour eux).

  2. Dans la fenêtre Piles parallèles, assurez-vous que le bouton Défilement automatique vers le frame de pile actif de la barre d’outils est activé.

    Lorsque vous possédez de grands diagrammes et que vous accédez au point d'arrêt suivant, vous souhaitez peut-être que la vue défile automatiquement vers le frame de pile actif du thread actuel, à savoir le thread qui a atteint en premier le point d'arrêt.

  3. Avant de continuer, dans la fenêtre Piles parallèles, défilez tout à gauche et tout en bas.

Continuez l'exécution jusqu'au quatrième point d'arrêt

  1. Pour reprendre l’exécution jusqu’au quatrième point d’arrêt, dans le menu Déboguer, sélectionnez Continuer.

    Remarquez comment la vue défile automatiquement. Basculez des threads dans la fenêtre Threads ou basculez des frames de pile dans la fenêtre Pile des appels. Notez comment la vue défile toujours automatiquement vers le frame approprié. Désactivez l’option Défilement automatique vers le frame de pile actif et notez la différence.

    La Vue aérienne est également utile avec les diagrammes de taille importante dans la fenêtre Piles parallèles. Par défaut, la Vue aérienne est activée. Vous pouvez activer et désactiver l’option en cliquant sur le bouton situé entre les barres de défilement en bas à droite de la fenêtre, comme indiqué dans l’illustration suivante.

    Screenshot of Birds eye view in Parallel Stacks window.

    Bird's-eye view in Parallel Stacks window

    Dans la vue aérienne, vous pouvez déplacer le rectangle pour obtenir un panoramique rapide autour du diagramme.

    Une autre façon de déplacer le diagramme consiste à sélectionner une zone vide de celui-ci et à le faire glisser où vous le souhaitez.

    Pour effectuer un zoom avant ou arrière dans le diagramme, appuyez sur CTRL et maintenez cette touche enfoncée pendant que vous déplacez la roulette de la souris. Vous pouvez également sélectionner le bouton Zoom de la barre d'outils, puis utiliser l'outil Zoom.

    Vous pouvez également afficher les piles dans le sens haut/bas, plutôt que bas/haut. Pour cela, dans le menu Outils, cliquez sur Options, puis activez ou désactivez l’option sous le nœud Débogage.

  2. Avant de continuer, dans le menu Déboguer, sélectionnez Arrêter le débogage pour arrêter l’exécution.

Utiliser la fenêtre Tâches parallèles et la vue Tâches de la fenêtre Piles parallèles

Nous vous recommandons d'effectuer les procédures précédentes avant de continuer.

Redémarrez l'application jusqu'à ce que le premier point d'arrêt soit atteint :

  1. Dans le menu Déboguer, sélectionnez Démarrer le débogage et attendez que le premier point d’arrêt soit atteint.

  2. Dans le menu Déboguer, pointez sur Fenêtres, puis sélectionnez Threads. Ancrez la fenêtre Threads en bas de Visual Studio.

  3. Dans le menu Déboguer, pointez sur Fenêtres et sélectionnez Pile des appels. Ancrez la fenêtre Pile des appels en bas de Visual Studio.

  4. Double-cliquez sur un thread dans la fenêtre Threads pour le rendre actuel. Les threads actuels comportent une flèche jaune. Lorsque vous modifiez le thread actuel, les autres fenêtres sont mises à jour. Nous allons maintenant examiner les tâches.

  5. Dans le menu Déboguer, pointez sur Fenêtres, puis sélectionne Tâches. L’illustration suivante présente la fenêtre Tâches.

    Screenshot of Four running tasks in Tasks window.

    Four running tasks in Tasks window

    Pour chaque tâche en cours, vous pouvez lire l’ID, retourné par la propriété de même nom, l’ID et le nom du thread qui l’exécute, ainsi que son emplacement (le fait de pointer dessus affiche une info-bulle qui comporte l’ensemble de la pile des appels). Sous la colonne Tâche, vous pouvez également voir la méthode qui a été passée dans la tâche, en d’autres termes le point de départ.

    Vous pouvez trier toutes les colonnes. Remarquez le glyphe de tri qui indique la colonne et le sens du tri. Vous pouvez également réorganiser les colonnes en les faisant glisser à gauche ou à droite.

    La flèche jaune indique la tâche actuelle. Vous pouvez basculer des tâches en double-cliquant dessus ou en utilisant le menu contextuel. Lorsque vous basculez des tâches, le thread sous-jacent devient le thread actuel et les autres fenêtres sont mises à jour.

    Quand vous passez manuellement d’une tâche à une autre, le contour de la flèche indique le contexte de débogueur actuel pour une tâche non actuelle.

    Lorsque vous basculez manuellement d'une tâche à une autre, la flèche jaune se déplace, mais une flèche blanche indique toujours la tâche qui a provoqué l'arrêt du débogueur.

Continuez l'exécution jusqu'au deuxième point d'arrêt

Pour reprendre l’exécution jusqu’au deuxième point d’arrêt, dans le menu Déboguer, sélectionnez Continuer.

Auparavant, la colonne État indiquait toutes les tâches comme étant à l’état actif, mais désormais deux tâches présentent l’état Bloqué. Les tâches peuvent être bloquées pour de nombreuses raisons. Dans la colonne État, pointez sur une tâche en attente pour savoir pourquoi elle est bloquée. Dans l’illustration suivante par exemple, la tâche 11 attend la tâche 12.

Screenshot of Two waiting tasks in Tasks window.

Auparavant, la colonne État indiquait toutes les tâches comme étant à l’état actif, mais désormais deux tâches présentent l’état Bloqué. Les tâches peuvent être bloquées pour de nombreuses raisons. Dans la colonne État, pointez sur une tâche en attente pour savoir pourquoi elle est bloquée. Par exemple, dans l’illustration suivante, la tâche 4 attend la tâche 5.

Two waiting tasks in Tasks window

La tâche 4, ensuite, attend un gestionnaire possédé par le thread assigné à la tâche 2. (Cliquez avec le bouton droit sur la ligne d’en-tête et choisissez Colonnes>Affectation de thread pour afficher la valeur d’affectation de thread pour la tâche 2).

Waiting task and tooltip in Tasks window

Vous pouvez signaler une tâche en cliquant sur l’indicateur dans la première colonne de la fenêtre Tâches.

Vous pouvez utiliser un indicateur pour effectuer le suivi des tâches entre différents points d’arrêt d’une même session de débogage ou pour filtrer les tâches dont les piles d’appels sont affichées dans la fenêtre Piles parallèles.

Quand vous avez utilisé la fenêtre Piles parallèles auparavant, vous avez affiché les threads d’application. Affichez à nouveau la fenêtre Piles parallèles mais, cette fois, visualisez les tâches d’application. Pour cela, sélectionnez Tâches dans la zone dans l’angle supérieur gauche. L’illustration suivante présente la vue Tâches.

Screenshot of Tasks view in Parallel Stacks window.

Tasks view in Parallel Stacks window

Les threads qui n’exécutent actuellement pas de tâches n’apparaissent pas dans la vue Tâches de la fenêtre Piles parallèles. En outre, pour les threads qui exécutent des tâches, certains des frames de pile qui ne sont pas associés aux tâches sont filtrés à partir du haut et bas de la pile.

Affichez à nouveau la fenêtre Tâches. Cliquez avec le bouton droit sur un en-tête de colonne pour afficher un menu contextuel pour cette colonne.

Vous pouvez utiliser le menu contextuel pour ajouter ou supprimer des colonnes. Par exemple, la colonne AppDomain n'est pas sélectionnée. Elle ne s'affiche donc pas dans la liste. Sélectionnez Parent. La colonne Parent s’affiche sans valeur pour chacune des quatre tâches.

Continuez l'exécution jusqu'au troisième point d'arrêt

Pour reprendre l’exécution jusqu’au troisième point d’arrêt, dans le menu Déboguer, sélectionnez Continuer.

Screenshot of Parent-child view in Tasks window.

Dans cet exemple d’exécution, notez que les tâches 11 et 12 s’exécutent sur le même thread (afficher la colonne Affectation de thread si elle est masquée). Ces informations ne sont pas affichées dans la fenêtre Threads. Leur affichage ici constitue un autre avantage de la fenêtre Tâches. Pour confirmer ceci, affichez la fenêtre Piles parallèles. Veillez à bien voir Tâches. Vous pouvez localiser les tâches 11 et 12 en analysant les info-bulles de la fenêtre Piles parallèles.

Task view in Parallel Stacks window

Une nouvelle tâche, la tâche 5, est en cours d’exécution et la tâche 4 est maintenant en attente. Vous pouvez voir pourquoi en pointant sur la tâche en attente dans la fenêtre État. Dans la colonne Parent, remarquez que la tâche 4 est la tâche parent de la tâche 5.

Pour mieux visualiser la relation parent-enfant, faites un clic droit sur la ligne des en-têtes de colonne, puis sélectionnez Vue Parent-enfant. L'illustration suivante doit apparaître.

Parent-child view in Tasks window

Notez que les tâches 4 et 5 s’exécutent sur le même thread (afficher la colonne Affectation de thread si elle est masquée). Ces informations ne sont pas affichées dans la fenêtre Threads. Leur affichage ici constitue un autre avantage de la fenêtre Tâches. Pour confirmer ceci, affichez la fenêtre Piles parallèles. Veillez à bien voir Tâches. Localisez les tâches 4 et 5 en double-cliquant dessus dans la fenêtre Tâches. La surbrillance bleue de la fenêtre Piles parallèles est alors mise à jour. Vous pouvez également localiser les tâches 4 et 5 en analysant les info-bulles de la fenêtre Piles parallèles.

Task view in Parallel Stacks window

Dans la fenêtre Piles parallèles, faites un clic droit sur S.P, puis sélectionnez Atteindre le thread. La fenêtre passe à la vue Threads et le frame correspondant s'affiche. Vous pouvez voir les deux tâches sur le même thread.

Highlighted thread in threads view

Ceci est un autre avantage de la vue Tâches de la fenêtre Piles parallèles par rapport à la fenêtre Threads.

Continuez l'exécution jusqu'au quatrième point d'arrêt

Pour reprendre l’exécution jusqu’au troisième point d’arrêt, dans le menu Déboguer, sélectionnez Continuer. Sélectionnez l’en-tête de colonne ID pour trier par ID. L'illustration suivante doit apparaître.

Screenshot of Four task states in Parallel Stacks window.

Les tâches 10 et 11 s’attendent maintenant réciproquement et sont bloquées. Plusieurs nouvelles tâches sont désormais planifiées. Les tâches planifiées sont des tâches qui ont été démarrées dans le code mais qui n'ont pas encore été exécutées. Par conséquent, leurs colonnes Emplacement et Affectation de thread sont vides ou affichent le message par défaut.

Four task states in Parallel Stacks window

Étant donné que la tâche 5 est terminée, elle n’est plus affichée. Si ce n'est pas le cas sur votre ordinateur et que l'interblocage n'est pas indiqué, appuyez sur F11.

Les tâches 3 et 4 s’attendent maintenant réciproquement et sont bloquées. Il y a également 5 nouvelles tâches qui sont des enfants de la tâche 2 et qui sont maintenant planifiées. Les tâches planifiées sont des tâches qui ont été démarrées dans le code mais qui n'ont pas encore été exécutées. Par conséquent, leurs colonnes Emplacement et Affectation de thread sont vides.

Ouvrez à nouveau la fenêtre Piles parallèles. L'en-tête de chaque zone comporte une info-bulle qui affiche les ID et noms de thread. Basculez en vue Tâches dans la fenêtre Piles parallèles. Pointez sur un en-tête pour afficher l'ID et le nom de la tâche, ainsi que son état, comme indiqué dans l'illustration suivante.

Header tooltip in Parallel Stacks window

Vous pouvez regrouper les tâches par colonne. Dans la fenêtre Tâches, faites un clic droit sur l’en-tête de colonne État, puis sélectionnez Regrouper par État. L’illustration suivante présente la fenêtre des Tâches regroupées par état.

Screenshot of Grouped tasks in Tasks window.

Grouped tasks in Tasks window

Vous pouvez également regrouper les tâches en fonction d'une autre colonne. Cela vous permet de vous concentrer sur un sous-ensemble de tâches. Chaque groupe réductible comporte un certain nombre des éléments regroupés ensemble.

La dernière fonctionnalité de la fenêtre Tâches à examiner est le menu contextuel qui s’affiche quand vous cliquez sur une tâche avec le bouton droit.

Ce menu contextuel contient différentes commandes en fonction de l’état de la tâche. Ces commandes peuvent inclure Copier, Sélectionner tout, Affichage hexadécimal, Basculer vers la tâche, Verrouiller le thread affecté, Verrouiller tous les threads sauf celui-ci, Libérer le thread affecté et Marquer.

Vous pouvez verrouiller le thread sous-jacent d'une ou plusieurs tâches, ainsi que verrouiller tous les threads sauf celui qui est assigné. Un thread verrouillé est représenté dans la fenêtre Tâches comme dans la fenêtre Threads par une icône pause bleue.

Résumé

Cette procédure pas à pas a présenté les fenêtres de débogage Tâches parallèles et Piles parallèles. Vous pouvez utiliser ces fenêtres sur de véritables projets qui utilisent du code multithread. Vous pouvez examiner le code parallèle rédigé en C++, C# ou Visual Basic.