Quelles leçons avez-vous tirées d'un projet qui a presque / échoué en raison d'un mauvais multithreading?
Parfois, le cadre impose un certain modèle de filetage qui rend les choses d'un ordre de grandeur plus difficiles à obtenir correctement.
Quant à moi, je n'ai pas encore récupéré du dernier échec et je pense qu'il vaut mieux pour moi de ne pas travailler sur tout ce qui a à voir avec le multithreading dans ce cadre.
J'ai trouvé que j'étais bon dans les problèmes de multithreading qui ont un simple fork / join, et où les données ne voyagent que dans une direction (alors que les signaux peuvent voyager dans une direction circulaire).
Je ne suis pas en mesure de gérer l'interface graphique dans laquelle certains travaux ne peuvent être effectués que sur un thread strictement sérialisé (le "thread principal") et d'autres travaux ne peuvent être effectués que sur n'importe quel thread mais le thread principal (les "threads de travail"), et où les données et les messages doivent voyager dans toutes les directions entre N composants (un graphique entièrement connecté).
Au moment où j'ai quitté ce projet pour un autre, il y avait des problèmes de blocage partout. J'ai entendu que 2-3 mois plus tard, plusieurs autres développeurs ont réussi à résoudre tous les problèmes de blocage, au point qu'il peut être expédié aux clients. Je n'ai jamais réussi à découvrir ce morceau de connaissances manquant qui me manque.
Quelque chose au sujet du projet: le nombre d'ID de message (valeurs entières qui décrivent la signification d'un événement qui peut être envoyé dans la file d'attente de messages d'un autre objet, quel que soit le filetage) atteint plusieurs milliers. Les chaînes uniques (messages utilisateur) se chiffrent également à environ un millier.
Ajoutée
La meilleure analogie que j'ai obtenue d'une autre équipe (sans rapport avec mes projets passés ou présents) était de "mettre les données dans une base de données". ("Base de données" se référant à la centralisation et aux mises à jour atomiques.) Dans une interface graphique qui est fragmentée en plusieurs vues s'exécutant toutes sur le même "thread principal" et tous les travaux lourds non GUI sont effectués dans des threads de travail individuels, les données de l'application doivent être stocké dans une seule plase qui agit comme une base de données et laisser la "base de données" gérer toutes les "mises à jour atomiques" impliquant des dépendances de données non triviales. Toutes les autres parties de l'interface graphique ne gèrent que le dessin d'écran et rien d'autre. Les parties de l'interface utilisateur peuvent mettre en cache des éléments et l'utilisateur ne remarquera pas s'il est périmé d'une fraction de seconde, s'il est correctement conçu. Cette "base de données" est également connue comme "le document" dans l'architecture Document-View. Malheureusement - non, mon application stocke toutes les données dans les vues. Je ne sais pas pourquoi c'était comme ça.
Collègues contributeurs:
(les contributeurs n'ont pas besoin d'utiliser des exemples réels / personnels. Les leçons tirées d'exemples anecdotiques, si vous les jugez crédibles, sont également les bienvenues.)
Réponses:
Ma leçon préférée - très durement gagnée! - est que dans un programme multithread le planificateur est un porc sournois qui vous déteste. Si les choses tournent mal, elles le feront, mais de façon inattendue. Si vous vous trompez, vous poursuivrez des heisenbugs étranges (car toute instrumentation que vous ajoutez modifiera les horaires et vous donnera un modèle d'exécution différent).
La seule façon sensée de résoudre ce problème est de corréler strictement toute la gestion des threads dans un morceau de code aussi petit et correct, ce qui est très conservateur pour garantir que les verrous sont correctement maintenus (et avec un ordre d'acquisition globalement constant également). . La façon la plus simple de le faire est de ne pas partager de mémoire (ou d'autres ressources) entre les threads, sauf pour la messagerie qui doit être asynchrone; qui vous permet d'écrire tout le reste dans un style sans fil. (Bonus: la mise à l'échelle sur plusieurs machines dans un cluster est beaucoup plus facile.)
la source
is that in a multithreaded program the scheduler is a sneaky swine that hates you.
- non, il fait exactement ce que vous lui avez dit de faire :)Voici quelques leçons de base auxquelles je peux penser en ce moment (pas à partir de projets qui échouent mais à partir de vrais problèmes vus sur de vrais projets):
la source
Nous avons hérité d'une partie où le projet GUI utilise une douzaine de threads. Cela ne donne que des problèmes. Interblocages, problèmes de course, appels GUI croisés ...
la source
Java 5 et versions ultérieures ont des exécuteurs qui sont destinés à faciliter la gestion des programmes de style de jointure de fourche multithread.
Utilisez-les, cela enlèvera beaucoup de douleur.
(et, oui, j'ai appris cela d'un projet :))
la source
J'ai une formation en systèmes embarqués durs en temps réel. Vous ne pouvez pas tester l'absence de problèmes causés par le multithreading. (Vous pouvez parfois confirmer la présence). Le code doit être prouvablement correct. Donc, meilleure pratique autour de toutes les interactions de threads.
la source
Une analogie d'un cours sur le multithreading que j'ai suivi l'année dernière m'a été très utile. La synchronisation des threads est comme un signal de trafic protégeant une intersection (données) contre l'utilisation simultanée de deux voitures (threads). L'erreur que beaucoup de développeurs font est de tourner les lumières rouges dans la plupart de la ville pour laisser passer une voiture parce qu'ils pensent qu'il est trop difficile ou dangereux de déterminer le signal exact dont ils ont besoin. Cela pourrait bien fonctionner lorsque le trafic est faible, mais entraînera un blocage à mesure que votre application se développera.
C'est quelque chose que je savais déjà en théorie, mais après ce cours, l'analogie est vraiment restée avec moi, et j'ai été étonné de voir combien de fois après cela j'enquêterais sur un problème de thread et trouverais une file d'attente géante, ou interromprait la désactivation partout lors d'une écriture dans une variable seuls deux threads ont été utilisés, ou les mutex ont été maintenus longtemps alors qu'ils pouvaient être refactorisés pour l'éviter complètement.
En d'autres termes, certains des pires problèmes de thread sont causés par une surpuissance essayant d'éviter les problèmes de thread.
la source
Essayez de recommencer.
Au moins pour moi, ce qui a créé une différence, c'est la pratique. Après avoir effectué plusieurs fois le travail multithread et distribué, vous obtenez juste le coup.
Je pense que le débogage est vraiment ce qui rend les choses difficiles. Je peux déboguer du code multithread en utilisant VS mais je suis vraiment complètement perdu si je dois utiliser gdb. Ma faute, probablement.
Une autre chose qui en apprend davantage sur les structures de données sans verrouillage.
Je pense que cette question peut être vraiment améliorée si vous spécifiez le cadre. Les pools de threads .NET et les travailleurs en arrière-plan sont vraiment différents de QThread, par exemple. Il y a toujours quelques problèmes spécifiques à la plate-forme.
la source
J'ai appris que les rappels des modules de niveau inférieur aux modules de niveau supérieur sont un énorme mal car ils provoquent l'acquisition de verrous dans un ordre opposé.
la source