web-dev-qa-db-fra.com

Comment détecter et déboguer les problèmes de multi-threading?

Ceci est un suivi de cette question , où je n'ai reçu aucune entrée sur ce point. Voici la brève question:

Est-il possible de détecter et de déboguer les problèmes provenant du code multi-thread?

Souvent, nous devons dire à nos clients: "Nous ne pouvons pas reproduire le problème ici, donc nous ne pouvons pas le résoudre. Veuillez nous indiquer les étapes pour reproduire le problème, puis nous le corrigerons." C'est une réponse en quelque sorte désagréable si je sais que c'est un problème multi-thread, mais surtout pas. Comment savoir qu'un problème est un problème multithread et comment le déboguer?

Je voudrais savoir s'il existe des cadres de journalisation spéciaux, ou des techniques de débogage, ou des inspecteurs de code, ou toute autre chose pour aider à résoudre ces problèmes. Les approches générales sont les bienvenues. Si une réponse doit être liée au langage, conservez-la sur .NET et Java.

62
MicSim

Problèmes de thread/simultanéité sont notoirement difficiles à reproduire - c'est l'une des raisons pour lesquelles vous devez concevoir pour éviter ou au moins minimiser les probabilités. C'est la raison pour laquelle les objets immuables sont si précieux. Essayez d'isoler les objets mutables sur un seul thread, puis contrôlez soigneusement l'échange d'objets mutables entre les threads. Essayez de programmer avec une conception de transfert d'objet plutôt qu'avec des objets "partagés". Pour ces derniers, utilisez des objets de contrôle entièrement synchronisés (qui sont plus faciles à raisonner) et évitez qu'un objet synchronisé utilise d'autres objets qui doivent également être synchronisés - c'est-à-dire, essayez de les garder autonomes. Votre meilleure défense est une bonne conception.

Deadlocks sont les plus faciles à déboguer, si vous pouvez obtenir une trace de pile en cas de blocage. Compte tenu de la trace, dont la plupart font la détection de blocage, il est facile d'identifier la raison, puis de raisonner sur le code pour savoir pourquoi et comment le corriger. Avec les blocages, il sera toujours difficile d'acquérir les mêmes verrous dans des ordres différents.

Les verrous en direct sont plus difficiles - être en mesure d'observer le système en état d'erreur est votre meilleur choix.

Conditions de course ont tendance à être extrêmement difficiles à reproduire, et sont encore plus difficiles à identifier à partir de l'examen manuel du code. Avec ceux-ci, le chemin que je prends habituellement, en plus des tests approfondis à reproduire, est de raisonner sur les possibilités et d'essayer de consigner des informations pour prouver ou réfuter les théories. Si vous avez des preuves directes de corruption de l'État, vous pourrez peut-être raisonner sur les causes possibles basées sur la corruption.

Plus le système est complexe, plus il est difficile de trouver des erreurs de concurrence et de raisonner sur son comportement. Utilisez des outils tels que JVisualVM et les profils de connexion à distance - ils peuvent vous sauver la vie si vous pouvez vous connecter à un système dans un état d'erreur et inspecter les threads et les objets.

Méfiez-vous également des différences de comportement qui dépendent du nombre de cœurs de processeur, de pipelines, de la bande passante du bus, etc. Les modifications du matériel peuvent affecter votre capacité à reproduire le problème. Certains problèmes n'apparaîtront que sur les processeurs monocœur, d'autres uniquement sur les multicœurs.

Une dernière chose, essayez d'utiliser des objets de concurrence distribués avec les bibliothèques système - par exemple dans Java Java.util.concurrent est votre ami. Écrire vos propres objets de contrôle d'accès simultané est difficile et lourd de danger; laissez-le aux experts, si vous avez le choix.

78
Lawrence Dol

Je pensais que le réponse vous avez obtenu à votre autre question était assez bon. Mais je vais insister sur ces points.

Modifier uniquement l'état partagé dans une section critique (exclusion mutuelle)

Acquérir des verrous dans un ordre défini et les libérer dans l'ordre inverse.

Utilisez autant que possible des abstractions pré-construites (comme les trucs dans Java.util.concurrent)

De plus, certains outils d'analyse peuvent détecter certains problèmes potentiels. Par exemple, FindBugs peut trouver des problèmes de thread dans les programmes Java. Ces outils ne peuvent pas trouver tous les problèmes (ils ne sont pas des puces d'argent) mais ils peuvent aider.

Comme vanslly le fait remarquer dans un commentaire à cette réponse, étudier une sortie de journalisation bien placée peut également être très utile, mais méfiez-vous de Heisenbugs .

7
Greg Mattes

Pour Java il y a un outil de vérification appelé javapathfinder que je trouve utile pour déboguer et vérifier l'application multi-threading contre les conditions de concurrence potentielle et les bugs de verrouillage de la mort du code .
Il fonctionne parfaitement avec Eclipse et Netbean IDE.

5
bLaXjack

En supposant que j'ai des rapports de problèmes difficiles à reproduire, je les trouve toujours en lisant du code, de préférence en lecture de code par paire, afin que vous puissiez discuter des besoins de verrouillage/sémantique de threading. Lorsque nous le faisons sur la base d'un problème signalé , je trouve que nous résolvons toujours un ou plusieurs problèmes assez rapidement. Je pense que c'est aussi une technique assez bon marché pour résoudre des problèmes difficiles.

Désolé de ne pas pouvoir vous dire d'appuyer sur ctrl + shift + f13, mais je ne pense pas qu'il y ait quelque chose comme ça disponible. Mais juste penser à ce que le problème signalé en fait est donne généralement une assez fort sens de l'orientation dans le code, vous n'avez donc pas à commencer par main ().

5
krosenvold

En plus des autres bonnes réponses que vous avez déjà obtenues: testez toujours sur une machine avec au moins autant de processeurs/cœurs de processeur que le client utilise, ou qu'il y a des threads actifs dans votre programme. Sinon, certains bogues multithreads peuvent être difficiles à impossibles à reproduire.

5
mghie

Outre les vidages sur incident, une technique consiste en une journalisation au moment de l'exécution étendue: où chaque thread enregistre ce qu'il fait.

La première question, lorsqu'une erreur est signalée, pourrait alors être: "Où est le fichier journal?"

Parfois, vous pouvez voir le problème dans le fichier journal: "Ce thread détecte un état illégal/inattendu ici ... et regardez, cet autre thread faisait cela, juste avant et/ou juste après cela."

Si le fichier journal ne dit pas ce qui se passe, alors présentez vos excuses au client, ajoutez suffisamment de déclarations de journalisation supplémentaires au code, donnez le nouveau code au client et dites que vous le corrigerez une fois de plus .

5
ChrisW

Parfois, les solutions multithread ne peuvent pas être évitées. S'il y a un bogue, il doit être étudié en temps réel, ce qui est presque impossible avec la plupart des outils comme Visual Studio. La seule solution pratique consiste à écrire des traces, bien que le traçage lui-même doive:

  1. n'ajoute aucun retard
  2. ne pas utiliser de verrouillage
  3. être multithread sûr
  4. retracer ce qui s'est passé dans le bon ordre.

Cela semble être une tâche impossible, mais elle peut être facilement réalisée en écrivant la trace dans la mémoire. En C #, cela ressemblerait à ceci:

public const int MaxMessages = 0x100;
string[] messages = new string[MaxMessages];
int messagesIndex = -1;

public void Trace(string message) {
  int thisIndex = Interlocked.Increment(ref messagesIndex);
  messages[thisIndex] = message;
}

La méthode Trace () est multithread safe, non bloquante et peut être appelée depuis n'importe quel thread. Sur mon PC, l'exécution prend environ 2 microsecondes, ce qui devrait être assez rapide.

Ajoutez des instructions Trace () partout où vous pensez que quelque chose pourrait mal se passer, laissez le programme s'exécuter, attendez que l'erreur se produise, arrêtez la trace, puis recherchez les éventuelles erreurs dans la trace.

Une description plus détaillée de cette approche qui collecte également des informations sur les threads et la synchronisation, recycle le tampon et génère la trace bien que vous pouvez trouver sur: CodeProject: Débogage du code multithread en temps réel 1

3
Peter Huber

Un petit tableau avec quelques techniques de débogage à prendre en compte lors du débogage de code multithread. Le graphique s'agrandit, veuillez laisser des commentaires et des conseils à ajouter. (mettre à jour le fichier à ce lien )

Multithreaded debugging chart

2
Mouze

J'ai implémenté l'outil vmlens pour détecter les conditions de concurrence dans les programmes Java pendant l'exécution. Il implémente un algorithme appelé gomme .

1
Thomas Krieger

Visual Studio vous permet d'inspecter la pile d'appels de chaque thread et vous pouvez basculer entre eux. Ce n'est en aucun cas suffisant pour suivre toutes sortes de problèmes de thread, mais c'est un début. De nombreuses améliorations pour le débogage multithread sont prévues pour le prochain VS2010.

J'ai utilisé WinDbg + SoS pour les problèmes de thread dans le code .NET. Vous pouvez inspecter les verrous (blocs de synchronisation), les piles d'appels de thread, etc.

1
Brian Rasmussen

le blog de Tess Ferrandez a de bons exemples d'utilisation de WinDbg pour déboguer les blocages dans .NET.

1
Sean

assert () est votre ami pour détecter les conditions de course. Chaque fois que vous entrez dans une section critique, affirmez que l'invariant qui lui est associé est vrai (c'est à cela que servent les CS). Bien que, malheureusement, le chèque puisse être coûteux et donc non adapté à une utilisation dans un environnement de production.

1
zvrba

J'ai fait face à un problème de thread qui donnait le MÊME mauvais résultat et ne se comportait pas de manière imprévisible car chaque fois que d'autres conditions (mémoire, planificateur, charge de traitement) étaient plus ou moins identiques.

D'après mon expérience, je peux dire que HARDEST PART consiste à reconnaître qu'il s'agit d'un problème de thread, et BEST SOLUTION consiste à examiner attentivement le code multi-thread. Juste en regardant attentivement le code du thread, vous devriez essayer de comprendre ce qui peut mal se passer. D'autres moyens (vidage de fil, profileur, etc.) viendront ensuite.

0
Kuldeep Tiwari

Développer du code la manière que Princess a recommandée pour votre autre question (Objets immuables et passage de message de style Erlang). Il sera plus facile de détecter les problèmes de multi-thread, car les interactions entre les threads seront bien définies.

0
Sean