Prise en main du débogage d’applications multithread (C#, Visual Basic et C++)
Visual Studio fournit plusieurs outils et éléments d’interface utilisateur qui aident à déboguer des applications multithread. 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 suivants fournissent des informations supplémentaires sur le recours à d’autres outils de débogage multithread :
Pour utiliser la barre d’outils Emplacement de débogage et la fenêtre Threads, consultez Procédure pas à pas : Débogage d’une application multithread.
Pour obtenir un exemple qui utilise Task (code managé) et le runtime d’accès concurrentiel (C++), consultez procédure pas à pas : Déboguer une application parallèle. Pour obtenir des conseils de débogage généraux qui s’appliquent à la plupart des types d’applications multithread, lisez les deux articles.
La première étape consiste à créer un projet d’application multithread.
Création d’un projet d’application multithread
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 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 le modèle Application console pour .NET ou C++, puis choisissez Suivant.
Note
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 Configurez 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 la version cible de .NET Framework recommandée ou .NET 8, puis choisissez Créer.
Un nouveau projet console s'affiche. Une fois le projet créé, un fichier source apparaît. En fonction du langage choisi, le fichier source peut être nommé Program.cs, MyThreadWalkthroughApp.cpp ou Module1.vb.
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."); } }
Dans le menu Fichier, sélectionnez Enregistrer tout.
(Visual Basic uniquement) Dans l’Explorateur de solutions (volet droit), cliquez avec le bouton droit sur le nœud du projet, puis choisissez Propriétés. Dans l’onglet Application, remplacez Objet de démarrage par Simple.
Débogage de l’application multithread
Dans l’éditeur de code source, recherchez l’extrait de code suivant :
Cliquez avec le bouton gauche dans la marge de 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 marge, un cercle rouge indique qu’un point d’arrêt est défini à cet emplacement.
Dans le menu Déboguer, sélectionnez Lancer le débogage (F5).
Visual Studio génère la solution. L’application commence à s’exécuter avec le débogueur, puis s’arrête au point d’arrêt.
Dans l’éditeur de code source, localisez la ligne contenant le point d'arrêt :
Découverte du marqueur de thread
Dans la barre d’outils Déboguer, sélectionnez le bouton Afficher les threads dans la source.
Appuyez deux fois sur F11 pour faire avancer le débogueur.
Examinez la reliure située sur le côté gauche de la fenêtre. Sur cette ligne, notez la présence d’une icône de marqueur de thread qui ressemble à deux threads tordus. Le marqueur de thread indique qu'un thread est interrompu à cet emplacement.
Un marqueur de thread peut être partiellement masqué par un point d’arrêt.
Placez le pointeur sur le marqueur de thread. 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 le cas présent, le nom est probablement
<noname>
.Sélectionnez le marqueur de thread pour afficher les options disponibles dans le menu contextuel.
Affichage de l’emplacement des threads
Dans la fenêtre Piles parallèles, vous pouvez basculer entre une vue Threads et (pour la programmation basée sur les tâches) une vue Tâches. Vous avez également la possibilité d’afficher les informations sur la pile des appels de chaque thread. Dans cette application, la vue Threads peut être utilisée.
Ouvrez la fenêtre Piles parallèles en choisissant Déboguer>Fenêtres>Piles parallèles. La fenêtre se présente comme suit. Les informations exactes peuvent varier en fonction de l’emplacement actuel de chaque thread, du matériel et du langage de programmation.
Dans cet exemple apparaissent les informations suivantes sur le code managé, de gauche à droite :
- Le thread actuel (flèche jaune) a entré
ServerClass.InstanceMethod
. Vous pouvez afficher l’ID de thread et le frame de pile d’un thread en pointant surServerClass.InstanceMethod
. - Le thread 31724 attend un verrou détenu par le thread 20272.
- Le thread principal (à gauche) s’est arrêté sur [Code externe], que vous pouvez afficher en détail si vous choisissez Afficher le code externe.
Dans cet exemple apparaissent les informations suivantes sur le code managé, de gauche à droite :
- 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 . - Deux threads, dont le thread actuel (flèche jaune), sont entrés dans
ServerClass.InstanceMethod
, tandis que l’autre thread s’est arrêté dansThread.Sleep
. - Un nouveau thread (à droite) démarre également, mais s’arrête sur
ThreadHelper.ThreadStart
.
- Le thread actuel (flèche jaune) a entré
Pour afficher les threads dans un affichage de liste, sélectionnez Déboguer>Windows>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.
Note
Pour plus d’informations sur l’utilisation de la fenêtre Threads, consultez Procédure pas à pas : débogage d’une application multithread.
Cliquez avec le bouton droit 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éfinition d’un espion dans une variable
Ouvrez la fenêtre Espion parallèle en sélectionnant Déboguer>Fenêtres>Espion parallèle>Espion parallèle 1.
Sélectionnez la cellule dans laquelle vous voyez le texte
<Add Watch>
(ou la cellule d’en-tête vide dans la quatrième colonne), puis entrezdata
.La valeur de la variable data de chaque thread s’affiche dans la fenêtre.
Sélectionnez la cellule dans laquelle vous voyez le texte
<Add Watch>
(ou la cellule d’en-tête vide dans la quatrième colonne), puis entrezcount
.La valeur de la variable
count
de chaque thread s’affiche dans la fenêtre. Si vous ne voyez pas encore toutes ces informations, appuyez plusieurs fois sur F11 pour faire avancer l’exécution des threads dans le débogueur.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 d’un indicateur pour effectuer le suivi des plus importants et ignorer les autres.
Dans la fenêtre Espion parallèle, maintenez la touche Maj enfoncée et sélectionnez plusieurs lignes.
Cliquez avec le bouton droit et sélectionnez Indicateur.
Tous les threads sélectionnés sont marqués d’un indicateur. Vous pouvez à présent filtrer les threads pour afficher uniquement ceux qui sont marqués d’un indicateur.
Dans la fenêtre Espion parallèle, sélectionnez le bouton Afficher uniquement les threads marqués d’un indicateur.
Seuls les threads marqués d’un indicateur apparaissent dans la liste.
Conseil
Une fois que vous avez marqué certains threads d’un indicateur, cliquez avec le bouton droit sur une ligne de code dans l’éditeur de code et choisissez Exécuter les threads marqués d’un indicateur au curseur. Veillez à choisir du code que tous les threads marqués d’un indicateur atteindront. Visual Studio interrompt les threads sur la ligne de code sélectionnée, ce qui permet de mieux contrôler l’ordre d’exécution en gelant et en libérant les threads.
Sélectionnez à nouveau le bouton Afficher uniquement les threads marqués d’un indicateur pour revenir au mode Afficher tous les threads.
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.
Gel et libération de l’exécution des threads
Conseil
Vous pouvez geler et libérer (suspendre et reprendre) des threads pour contrôler l’ordre dans lequel ils effectuent le travail. Cela peut vous aider à résoudre les problèmes d’accès concurrentiel, notamment les blocages et les conditions de concurrence.
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. Elle indique que le thread est gelé.
Désélectionnez toutes les autres lignes en sélectionnant une seule ligne.
Cliquez avec le bouton droit sur une ligne et sélectionnez Libérer.
L’icône de pause disparaît sur cette ligne, ce qui indique que le thread n’est plus gelé.
Basculez vers l’éditeur de code et appuyez sur F11. Seul le thread libéré s’exécute.
Il se peut aussi que l’application instancie 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. Pour ce faire, vous pouvez geler les threads qui ne vous intéressent pas. Certains scénarios imposent toutefois de suivre un seul thread sans geler les autres, par exemple pour reproduire un bogue en particulier. Vous devez alors éviter d’entrer dans le code en dehors du thread qui vous intéresse. Définissez pour cela un point d’arrêt conditionnel.
Vous pouvez définir des points d’arrêt sur différentes conditions, notamment le nom du thread ou son ID. 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 par n’importe quel thread particulier.
Cliquez avec le bouton droit sur le point d’arrêt que vous avez créé, puis sélectionnez Conditions.
Dans la fenêtre Paramètres du point d’arrêt, entrez
data == 5
comme expression conditionnelle.Conseil
Si vous vous intéressez plutôt à un thread spécifique, utilisez son nom ou son ID comme condition. Pour ce faire, sélectionnez Filtre au lieu d’Expression conditionnelle dans la fenêtre Paramètres du point d’arrêt, puis suivez les conseils de filtre. Il peut être judicieux de nommer vos threads dans le code de votre application, car les ID de threads changent lorsque vous redémarrez le débogueur.
Fermez la fenêtre Paramètres du point d’arrêt.
Sélectionnez le bouton Redémarrer pour relancer votre session de débogage.
Vous entrez dans le code sur le thread à l’endroit où la valeur de la variable de données est 5. Dans la fenêtre Surveillance parallèle, recherchez la flèche jaune indiquant le contexte actuel du débogueur.
Vous pouvez maintenant exécuter le code pas à pas (F10) et entrer dans le code (F11) pour suivre l’exécution du thread unique.
Tant que la condition de point d’arrêt est propre au thread et que le débogueur n’atteint pas d’autres points d’arrêt sur d’autres threads (vous devrez peut-être les désactiver), vous pouvez exécuter le code pas à pas et entrer dans le 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.