Y a-t-il des moyens de tester votre code multi-threadé pour les conditions de course et les blocages?
Pour voir s'ils effectuent la façon dont ils devraient être ...
ÉCHECS , un projet de recherche Microsoft. Citant leur site:
Les échecs sont un outil permettant de trouver et de reproduire des heisenbugs dans des programmes simultanés. Les échecs gèrent à plusieurs reprises un test simultané garantissant que chaque exécution prend un entrelacement différent. Si un entrelacement entraîne une erreur, les échecs peuvent reproduire l'entrelacement d'amélioration du débogage. Les échecs sont disponibles pour les programmes gérés et indigènes.
Mise à jour (9/23/2015): Pour C, C++ et Go, vous pouvez utiliser Threadsanitizer .
Valgrind a Helgrind qui aide vraiment. Non seulement cela aidait-il à souligner les races qui pourraient conduire à la famine ou à une impasse, le léger ralentissement du programme profilé parfois expose parfois des races qui pourraient ne pas être observées autrement.
Donc, même si vous allez commando avec une sorte de méthode sans verrouillage, cela aide toujours :)
C'est POSIX CENTRIC, cependant. Il est livré avec des en-têtes qui simplifient facilement des bibliothèques de test unitaires telles que TAP CADEND qu'il est en cours d'exécution, ce qui est également vraiment utile. Par exemple, vous pourriez avoir un fil qui ne bloquerait normalement pas lorsque vous essayez d'acquérir une serrure aller de l'avant et de bloquer (peut-être au hasard), juste pour simuler la famine.
Je ne me souviens pas des détails exactement, mais c'est l'idée générale. Et je ne l'ai fait qu'une seule fois, mais ce que j'ai fait était séparer le code de re-entrant du code exécutant la tâche, à l'aide d'une interface pour pouvoir se moquer de la classe de tâches.
Ensuite, j'ai conçu ma maquette pour pouvoir verrouiller sur un appel afin que je sache que le thread est dans la section critique, puis appelez-le à nouveau et vérifiez qu'il attend, avant de libérer le premier fil et de finir proprement.
Quelque chose comme ca.
Je ne suis pas sûr que cela fonctionnerait pour des scénarios plus complexes, mais cela aide à préserver le comportement pendant les refacteurs.
À Jaoo/Goto cette année, j'ai vu cette présentation:
L'astuce est que vous modélisez ce que votre application de bobines de poils est censée faire, en termes d'étapes d'invocation ainsi que des opérations réelles de votre application. Le logiciel John Hughes essaie ensuite systématiquement de nombreuses permutations d'étapes d'invocation à plusieurs reprises dans parallèle et vérifie ensuite que l'état de l'application correspond à l'état du modèle. Si une erreur est trouvée, le logiciel sait comment réduire les étapes à l'affaire minimale produisant l'erreur.
Il a démontré de savoir comment attraper plusieurs bugs dans les bibliothèques de base erlang qui se caillaient pendant 15 ans et de temps en temps rapportés, mais personne ne pouvait comprendre d'où ils venaient et que de la manière de corriger. Avec les cas minimaux rapportés par le logiciel, le responsable de la bibliothèque a pu fixer chaque bogue dans un jour .
C'était SO impressionnant.
John Hughes vend ce logiciel à travers sa société.
Vous pouvez essayer mon Détecteur de course de relacie . Il est conçu pour vérifier avec soin et précisément vérifier les algorithmes de synchronisation telles que les files d'attente de producteurs-consommateurs et les conteneurs simultanés, mais pas très adapté à la vérification de programmes entiers. Cependant, peut-être que c'est une bonne idée de diffuser de la synchronisation et des mutiles d'un programme de toute façon, mais de concentrer la synchronisation dans des composants spécialisés (pouvant être vérifiée avec relacement).
Ce n'est pas facile, mais fondamentalement, le seul moyen est d'appeler le code multi-threadé simultanément de plusieurs threads et modifier le calendrier et de commander au hasard en jouant avec aléatoire Thread.sleep()
et Thread.yield()
] appelle (supposant Java).
Il existe également des outils prêts disponibles (tels que Testng) qui font quelque chose comme décrit ci-dessus, mais ils ne sont pas encore très matures, autant que je sache.
Pas un test d'unité strict, mais un chèque d'exécution qui m'a aidé avec des tests de défaillance par intermittence. C'est rapide et sale, mais cela a fonctionné.
Quand un mutex est accordé, je garde une trace de quel fil l'a. Toutes les demandes de mutex ont une trente-seconde foisout, après quoi elles crient impasse.
Je peux ensuite utiliser la liste des mauîtres accordés pour voir quel fil détient le mutex de blocage et pourquoi pendant si longtemps. Dans mes cas Jusqu'à présent, c'est parce que c'était une mauvaise manche sur autre chose, alors je peux alors résoudre cette situation.
Cela a fonctionné pour moi car mes mutiles ont une classe d'enveloppe multiplate-forme qui facilite l'injection de la conservation des enregistrements et du délai d'attente. Je connaissais aussi assez de l'application pour savoir qu'elle ne devrait jamais bloquer sur un mutex pendant 30 secondes.
Cela pourrait ne pas être complètement général, mais cela permet d'économiser beaucoup de débogage pendant environ deux heures d'effort de programmation. Les frais généraux sont négligeables, et cela peut être débogué-construire uniquement.
Je vais regarder en l'extension pour enregistrer des séquences de demande mutex imbriquées, et voir s'il s'agit de l'induction potentiellement de l'impasse (par exemple, un thread verrouille A alors b et une autre serrure B puis a) plutôt que simplement induisant l'impasse, mais jusqu'à présent Cela fait beaucoup d'avantages pour des efforts triviaux.