Partager via


Commencez le débogage des applications multithreads (C#, Visual Basic, C++)

Visual Studio fournit plusieurs outils et éléments d’interface utilisateur pour vous aider à déboguer des applications multithreads. Ce tutoriel montre comment utiliser les marqueurs de thread, la fenêtre Piles parallèles, la fenêtre Espion parallèle, les points d’arrêt conditionnels et les points d’arrêt de filtre. Vous vous familiarisez ainsi avec les fonctionnalités de débogage d’applications multithread de Visual Studio.

Les deux articles fournissent des informations supplémentaires sur le recours à d’autres outils de débogage multithread :

La première étape consiste à créer un projet d’application multi-tâches.

Création d’un projet d’application multithread

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

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

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

    Dans la fenêtre Créer un projet , entrez ou tapez la console dans la zone de recherche. Ensuite, choisissez C#, C++ ou Visual Basic dans la liste de langues, puis choisissez Windows dans la liste plateforme.

    Après avoir appliqué les filtres de langue et de plateforme, choisissez le modèle d’application console pour .NET ou C++, puis choisissez Suivant.

    Remarque

    Si vous ne voyez pas le modèle approprié, accédez à ToolsGet Tools >and Features..., qui ouvre Visual Studio Installer. Choisissez le développement de bureau .NET ou le développement Desktop avec la charge de travail C++ , puis choisissez Modifier.

    Dans la fenêtre Configurer votre nouveau projet , tapez ou entrez MyThreadWalkthroughApp dans la zone Nom du projet . Ensuite, choisissez Suivant ou Créer, selon l’option disponible.

    Pour un projet .NET Core ou .NET 5+, 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. Selon la langue que vous avez choisie, le fichier source peut être appelé Program.cs, MyThreadWalkthroughApp.cpp ou Module1.vb.

  2. Supprimez le code qui apparaît dans le fichier source et remplacez-le par le code mis à jour suivant. Choisissez l’extrait de code approprié pour votre configuration de code.

    using System;
    using System.Threading;
    
    public class ServerClass
    {
    
        static int count = 0;
        // The method that will be called when the thread is started.
        public void InstanceMethod()
        {
            Console.WriteLine(
                "ServerClass.InstanceMethod is running on another thread.");
    
            int data = count++;
            // Pause for a moment to provide a delay to make
            // threads more apparent.
            Thread.Sleep(3000);
            Console.WriteLine(
                "The instance method called by the worker thread has ended. " + data);
        }
    }
    
    public class Simple
    {
        public static void Main()
        {
            for (int i = 0; i < 10; i++)
            {
                CreateThreads();
            }
        }
        public static void CreateThreads()
        {
            ServerClass serverObject = new ServerClass();
    
            Thread InstanceCaller = new Thread(new ThreadStart(serverObject.InstanceMethod));
            // Start the thread.
            InstanceCaller.Start();
    
            Console.WriteLine("The Main() thread calls this after "
                + "starting the new InstanceCaller thread.");
    
        }
    }
    
  3. Dans le menu Fichier, sélectionnez Enregistrer tout.

  4. (Visual Basic uniquement) Dans l’Explorateur de solutions (volet droit), cliquez avec le bouton droit sur le nœud du projet, choisissez Propriétés. Sous l’onglet Application , remplacez l’objet Startup par Simple.

Déboguer l’application multi-threadée

  1. Dans l’éditeur de code source, recherchez l’extrait de code suivant :

    Thread.Sleep(3000);
    Console.WriteLine();
    
  2. Cliquez avec le bouton gauche dans la barre gauche de l’instruction Thread.Sleep ou, pour C++, std::this_thread::sleep_for pour insérer un nouveau point d’arrêt.

    Dans la gouttière, un cercle rouge indique qu’un point d’arrêt est défini à cet emplacement.

  3. Dans le menu Débogage , sélectionnez Démarrer le débogage (F5).

    Visual Studio génère la solution, l’application commence à s’exécuter avec le débogueur attaché, puis l’application s’arrête au point d’arrêt.

  4. Dans l’éditeur de code source, recherchez la ligne qui contient le point d’arrêt.

Découvrir le marqueur de fil

  1. Dans la barre d’outils de débogage, sélectionnez le bouton Afficher les threads dans la sourceShow Threads in Source.

  2. Appuyez deux fois sur F11 pour faire avancer le débogueur.

  3. Regardez la gouttière sur le côté gauche de la fenêtre. Sur cette ligne, notez une icône de marqueur de threadThread Marker qui ressemble à deux fils tordus. Le marqueur de thread indique qu’un thread est arrêté à cet emplacement.

    Un marqueur de thread peut être partiellement masqué par un point d’arrêt.

  4. Survolez le pointeur sur le marqueur de fil. Un conseil sur les données apparaît, indiquant le nom et le numéro d’ID de chacun des threads arrêtés. Dans ce cas, le nom est probablement <noname>.

    Capture d’écran de l’ID du thread dans un DataTip.

  5. Sélectionnez le marqueur de thread pour afficher les options disponibles dans le menu contextuel.

Afficher les emplacements des threads

Dans la fenêtre Stacks parallèles, vous pouvez basculer entre une vue Threads et une vue Tâches (pour la programmation basée sur des tâches) et afficher les informations de pile des appels pour chaque thread. Dans cette application, nous pouvons utiliser la vue Threads.

  1. Ouvrez la fenêtre Piles parallèles en choisissant Déboguer>Windows>Piles parallèles. Vous devriez voir quelque chose de similaire à ce qui suit. Les informations exactes peuvent varier en fonction de l’emplacement actuel de chaque thread, de votre matériel et de votre langage de programmation.

    Capture d’écran de la fenêtre des piles parallèles.

    Dans cet exemple, de gauche à droite, nous voyons ces informations pour le code managé :

    • Le thread actuel (flèche jaune) est entré en ServerClass.InstanceMethod. Vous pouvez afficher l’ID de thread et le cadre de pile d’un thread en pointant sur ServerClass.InstanceMethod.
    • Thread 31724 attend un verrou appartenant à Thread 20272.
    • Le thread principal (côté gauche) s’est arrêté sur [Code externe], que vous pouvez afficher en détail si vous choisissez Afficher le code externe.

    Fenêtre Piles parallèles

    Dans cet exemple, de gauche à droite, nous voyons ces informations pour le code managé :

    • Le thread principal (côté gauche) s’est arrêté sur Thread.Start, où le point d’arrêt est identifié par l’icône de marqueur de thread Marqueur de thread..
    • Deux threads sont entrés dans ServerClass.InstanceMethod, dont l'un est le thread actuel (flèche jaune), tandis que l'autre thread s'est arrêté dans Thread.Sleep.
    • Un nouveau thread (à droite) démarre également mais est arrêté sur ThreadHelper.ThreadStart.
  2. Pour afficher les threads dans une vue en liste, sélectionnez Déboguer>Windows>Threads.

    Capture d’écran de la fenêtre Threads.

    Dans cette vue, vous pouvez facilement voir que le thread 20272 est le thread principal et se trouve actuellement dans du code externe, en particulier System.Console.dll.

    Remarque

    Pour plus d’informations sur l’utilisation de la fenêtre Threads, consultez Procédure pas à pas : déboguer une application multithread.

  3. Cliquez avec le bouton droit sur les entrées dans la fenêtre Piles parallèles ou Threads pour afficher les options disponibles dans le menu contextuel.

    Vous pouvez effectuer différentes actions à partir de ces menus contextuels. Pour ce tutoriel, vous allez explorer plus de détails dans la fenêtre Parallel Watch (sections suivantes).

Définir une surveillance sur une variable

  1. Ouvrez la fenêtre Parallel Watch en sélectionnant Debug>Windows>Espion parallèle>Espion parallèle 1.

  2. Sélectionnez la cellule dans laquelle vous voyez le <Add Watch> texte (ou la cellule d’en-tête vide dans la quatrième colonne) et entrez data.

    Les valeurs de la variable de données pour chaque thread apparaissent dans la fenêtre.

  3. Sélectionnez la cellule dans laquelle vous voyez le <Add Watch> texte (ou la cellule d’en-tête vide dans la cinquième colonne) et entrez count.

    Les valeurs de la count variable pour chaque thread s’affichent dans la fenêtre. Si vous ne voyez pas encore ces informations, essayez d’appuyer sur F11 plusieurs fois pour avancer l’exécution des threads dans le débogueur.

    Fenêtre Espion parallèle.

  4. Cliquez avec le bouton droit sur l’une des lignes de la fenêtre pour afficher les options disponibles.

Ajouter et supprimer des threads

Vous pouvez marquer des threads pour suivre les threads importants et ignorer les autres threads.

  1. Dans la fenêtre Espion parallèle , maintenez la touche Maj enfoncée et sélectionnez plusieurs lignes.

  2. Cliquez avec le bouton droit et sélectionnez Indicateur.

    Tous les threads sélectionnés sont marqués. À présent, vous pouvez filtrer pour afficher uniquement les threads avec indicateur.

  3. Dans la fenêtre Surveillance parallèle, sélectionnez le bouton Afficher uniquement les threads avec indicateurAfficher les threads avec indicateur.

    Seuls les threads avec indicateur apparaissent dans la liste.

    Conseil / Astuce

    Une fois que vous avez marqué certains threads, vous pouvez cliquer avec le bouton droit sur une ligne de code dans l’éditeur de code et choisir Exécuter des threads avec indicateur sur curseur. Veillez à choisir du code que tous les threads marqués d’un indicateur atteindront. Visual Studio suspend les threads sur la ligne de code sélectionnée, ce qui facilite le contrôle de l’ordre d’exécution en gelant et en dégelant les threads.

  4. Sélectionnez à nouveau le bouton Afficher uniquement les threads avec indicateur pour revenir au mode Afficher tous les threads .

  5. Pour supprimer l’indicateur d’un ou de plusieurs threads, cliquez avec le bouton droit sur le ou les threads concernés dans la fenêtre Espion parallèle, puis sélectionnez Supprimer l’indicateur.

Figer et dégeler l’exécution des threads

Conseil / Astuce

Vous pouvez figer et dégeler (suspendre et reprendre) des threads pour contrôler l’ordre dans lequel les threads effectuent le travail. Cela peut vous aider à résoudre les problèmes de concurrence tels que les conditions de course et les blocages.

  1. Dans la fenêtre Surveillance parallèle, sélectionnez toutes les lignes, cliquez avec le bouton droit et sélectionnez Geler.

    Dans la deuxième colonne, une icône de pause s’affiche pour chaque ligne. L’icône de pause indique que le thread est figé.

  2. Désélectionnez toutes les autres lignes en sélectionnant une seule ligne.

  3. Cliquez avec le bouton droit sur une ligne, puis sélectionnez Thaw.

    L’icône de pause disparaît sur cette ligne, ce qui indique que le thread n’est plus figé.

  4. Basculez vers l’éditeur de code et appuyez sur F11. Seul le thread libéré s’exécute.

    L’application peut également instancier de nouveaux threads. Ceux-ci ne sont ni marqués d’un indicateur, ni gelés.

Suivi d’un seul thread avec des points d’arrêt conditionnels

Il est parfois utile de suivre l’exécution d’un seul thread dans le débogueur. Une façon de le faire consiste à geler les threads qui ne vous intéressent pas. Dans certains scénarios, vous devrez peut-être suivre un thread unique sans figer d’autres threads, par exemple pour reproduire un bogue particulier. Pour suivre un thread sans figer d’autres threads, vous devez éviter d'intervenir dans le code, excepté sur le thread qui vous intéresse. Vous pouvez effectuer cette tâche en définissant un point d’arrêt conditionnel.

Vous pouvez définir des points d’arrêt dans différentes conditions, comme le nom du thread ou l’ID de thread. Il peut être utile de spécifier la condition sur des données dont vous savez qu’elles sont propres à chaque thread. Cette approche est courante lors du débogage lorsque vous êtes plus intéressé par une valeur de données particulière que dans n’importe quel thread particulier.

  1. Cliquez avec le bouton droit sur le point d’arrêt que vous avez créé précédemment et sélectionnez Conditions.

  2. Dans la fenêtre Paramètres de point d'arrêt, entrez data == 5 pour l’expression conditionnelle.

    Point d'arrêt conditionnel

    Conseil / Astuce

    Si vous êtes plus intéressé par un thread spécifique, utilisez un nom de thread ou un ID de thread pour la condition. Pour ce faire, dans la fenêtre Paramètres du point d'arrêt, sélectionnez Filtrer au lieu de Expression conditionnelle, puis suivez les instructions relatives au filtre. Vous pouvez nommer vos threads dans le code de votre application, à mesure que les ID de threads changent lorsque vous redémarrez le débogueur.

  3. Fermez la fenêtre Paramètres du point d’arrêt .

  4. Sélectionnez le bouton Redémarrer l’application pour redémarrer votre session de débogage.

    Vous allez entrer dans le code sur le thread à l’endroit où la valeur de la variable data est 5. Dans la fenêtre Parallel Watch, cherchez la flèche jaune qui indique le contexte de débogage actuel.

  5. À présent, vous pouvez parcourir le code (F10) et passer au code (F11) et suivre l’exécution du thread unique.

    Tant que la condition de point d’arrêt est unique pour le thread et que le débogueur n’atteint aucun autre point d’arrêt sur d’autres threads (vous devrez peut-être les désactiver), vous pouvez parcourir le code et passer au code sans basculer vers d’autres threads.

    Remarque

    Tous les threads s’exécutent lorsque vous faites avancer le débogueur. Toutefois, celui-ci n’entre dans le code sur d’autres threads que si l’un d’entre eux atteint un point d’arrêt.