Considérons quelque chose comme une application d'interface graphique où le thread principal met à jour l'interface utilisateur presque instantanément, et un autre thread interroge des données sur le réseau ou quelque chose qui prend 5 à 10 secondes pour terminer le travail.
J'ai reçu de nombreuses réponses différentes à ce sujet, mais certaines personnes disent que s'il s'agit d'une situation critique d'une impossibilité statistique, ne vous inquiétez pas du tout, mais d'autres ont dit que s'il y avait même une marge de 10 -53 % (je vous n’avez pas de chiffres, c’est ce que j’ai entendu) d’une certaine magie vaudou se produisant en raison de conditions de concurrence critique, obtenez / libérez toujours des verrous sur le fil qui en a besoin.
Quelles sont vos pensées? Est-ce une bonne pratique de programmation de gérer la situation de concurrence critique dans de telles situations statistiquement impossibles? ou serait-il totalement inutile ou même contre-productif d'ajouter plus de lignes de code pour empêcher la lisibilité?
Réponses:
S'il s'agit vraiment d'un événement 1 sur 10 ^ 55, il ne serait pas nécessaire de coder pour cet événement. Cela impliquerait que si vous exécutiez l'opération 1 million de fois par seconde, vous auriez un bogue tous les 3 * 10 ^ 41 ans, ce qui correspond approximativement à 10 ^ 31 fois l'âge de l'univers. Si votre application n'a d'erreur qu'une seule fois sur chaque billion de milliards d'âges de l'univers, c'est probablement assez fiable.
Cependant, je parierais très fort que l'erreur est loin d'être aussi improbable. Si vous pouvez concevoir l’erreur, il est presque certain qu’elle se produira au moins de temps en temps, ce qui vous permet de coder correctement pour commencer. De plus, si vous codez correctement les threads au début pour qu'ils obtiennent et libèrent les verrous de manière appropriée, le code est beaucoup plus facile à gérer dans le futur. Lorsque vous apportez un changement, vous n'avez pas à vous soucier de réanalyser toutes les conditions de concurrence potentielles, de recalculer leurs probabilités et de vous assurer qu'elles ne se reproduiront pas.
la source
Du point de vue des coûts et des avantages, vous devez écrire du code supplémentaire uniquement lorsque vous en retirez un bénéfice suffisant.
Par exemple, si le pire scénario qui se produirait si un fil de discussion incorrect "remporte la course" est que les informations ne s'affichent pas et que l'utilisateur doit cliquer sur "actualiser", ne vous inquiétez pas de la situation de concurrence critique: devoir écrire beaucoup de code ne vaut pas la peine de réparer quelque chose d'insignifiant.
D'autre part, si la condition de concurrence critique peut entraîner des transferts de fonds incorrects entre comptes bancaires, vous devez vous prémunir contre la condition de concurrence financière, peu importe la quantité de code que vous devez écrire pour résoudre ce problème.
la source
Trouver une condition de concurrence est la partie la plus difficile. Vous avez probablement passé presque autant de temps à écrire cette question qu’il vous aurait fallu pour la résoudre. Ce n'est pas comme si cela le rendait beaucoup moins lisible. Les programmeurs s'attendent à voir le code de synchronisation dans de telles situations et pourraient en fait perdre plus de temps à se demander pourquoi il n'est pas là et si l'ajouter le corrigerait pour résoudre leur bogue sans rapport.
En ce qui concerne les probabilités, vous seriez surpris. L'année dernière, j'avais reçu un rapport de bogue sur l'état des conditions de course que je ne pouvais pas reproduire avec des milliers d'essais automatisés, mais un système d' un client le voyait tout le temps. La valeur commerciale de consacrer 5 minutes à résoudre ce problème maintenant, par opposition à la résolution d'un problème "impossible" lors de l'installation d'un client, facilite grandement le choix.
la source
Obtenez et libérez les serrures. Les probabilités changent, les algorithmes changent. C'est une mauvaise habitude, et quand quelque chose ne va pas, vous ne devez pas vous arrêter et vous demander si vous ne vous trompez pas…
la source
Jusqu'à ce que quelqu'un introduit une couche de mise en cache pour améliorer les performances. Soudainement, cette autre bande de roulement finissait presque instantanément et la situation de concurrence manifeste le plus souvent.
Si cela avait eu lieu il y a quelques semaines, il a fallu environ 2 jours complets de développement pour trouver le bogue.
Fixez toujours les conditions de course si vous les reconnaissez.
la source
Simple vs correct.
Dans de nombreux cas, la simplicité l'emporte sur la correction. C'est une question de coût.
De plus, les conditions de course sont des choses désagréables qui ont tendance à ne pas obéir à de simples statistiques. Tout se passe bien jusqu'à ce qu'une autre synchronisation, apparemment sans lien, provoque soudainement votre situation critique dans la moitié des cas. À moins d’activer les journaux ou de déboguer le code, bien sûr.
Une alternative pragmatique à la prévention d'une situation de concurrence critique (qui peut être délicate) peut être de la détecter et de la journaliser (bonus pour échec rapide et précoce). Si cela ne se produit jamais, vous avez peu perdu. Si cela se produit réellement, vous avez une justification solide pour passer le temps supplémentaire à le réparer.
la source
Si votre condition de course est liée à la sécurité, vous devriez toujours coder pour l'empêcher.
Un exemple courant est les conditions de concurrence avec la création / ouverture de fichiers sous Unix, qui peuvent dans certaines circonstances donner lieu à des attaques par élévation de privilèges si le programme avec la condition de concurrence s'exécute avec des privilèges plus élevés que ceux qui interagissent avec lui, tel qu'un processus de démon système ou pire encore, le noyau.
Même si une situation de concurrence a environ 10% (- 80) chances de se produire de manière aléatoire , il est tout à fait possible qu'un attaquant déterminé ait une chance décente de créer de telles conditions délibérément et artificiellement.
la source
Therac-25!
Les développeurs du projet Therac-25 étaient plutôt confiants quant au décalage entre une interface utilisateur et un problème d’interface dans une machine thérapeutique XRAY.
Ils n'auraient pas dû être.
Vous pouvez en apprendre plus sur ce fameux désastre logiciel à l’adresse suivante:
http://www.youtube.com/watch?v=izGSOsAGIVQ
ou
http://en.wikipedia.org/wiki/Therac-25
Votre application risque d’être beaucoup moins sensible à l’échec que les dispositifs médicaux. Une méthode utile consiste à évaluer l'exposition au risque comme le produit de la probabilité d'occurrence et du coût de l'occurrence pendant la durée de vie du produit pour toutes les unités pouvant être produites.
Si vous avez choisi de construire votre code pour qu'il dure (et cela ressemble à vous), vous devriez considérer la loi de Moore qui peut facilement supprimer plusieurs zéros toutes les quelques années à mesure que les ordinateurs à l'intérieur ou à l'extérieur de votre système deviennent plus rapides. Si vous expédiez des milliers d'exemplaires, supprimez plus de zéros. Si les utilisateurs effectuent cette opération quotidiennement (ou mensuellement) pendant des années, en emportez quelques-uns de plus. S'il est utilisé là où la fibre de Google est disponible, quoi alors? Si les ordures de l'interface utilisateur collectent une opération de l'interface graphique moyenne, cela affecte-t-il la course? Utilisez-vous une bibliothèque Open Source ou Windows derrière votre interface graphique? Les mises à jour peuvent-elles affecter le timing?
Les sémaphores, les verrous, les mutex, la synchronisation de barrières sont parmi les moyens de synchroniser les activités entre les threads. Potentiellement, si vous ne les utilisez pas, une autre personne chargée de la maintenance de votre programme pourrait très rapidement changer d'hypothèse sur les relations entre les threads et invalider le calcul de la condition de concurrence critique.
Je recommande que vous synchronisiez explicitement parce que même si vous ne le voyez pas créer un problème, un client peut le faire. En outre, même si votre condition de concurrence n’est jamais remplie, que se passe-t-il si vous ou votre organisation êtes poursuivis en justice pour défendre votre code (comme Toyota était apparenté à la Prius il y a quelques années). Plus votre méthodologie est approfondie, mieux vous vous en tirerez. Il serait peut-être plus agréable de dire "nous nous gardons contre ce cas improbable comme celui-ci ..." plutôt que de dire "nous savons que notre code va échouer, mais nous avons écrit cette équation pour montrer que cela ne se produira pas de notre vivant. Probablement. "
Il semble que le calcul de probabilité vienne de quelqu'un d'autre. Connaissent-ils votre code et le connaissez-vous suffisamment pour avoir confiance qu'aucune erreur n'a été commise? Si je calculais une fiabilité de 99,99997% pour quelque chose, je pourrais aussi repenser à mes cours de statistiques universitaires et me rappeler que je n’obtenais pas toujours 100% et que je reculais de quelques pour cent sur mes propres estimations de fiabilité.
la source
La simplicité n'est bonne que si elle est également correcte. Comme ce code n'est pas correct, les futurs programmeurs l' examineront inévitablement lorsqu'ils rechercheront un bogue associé.
Quelle que soit la façon dont vous le gérez (en le consignant, en le documentant ou en ajoutant des verrous - cela dépend du coût), vous épargnerez du temps aux autres programmeurs lors de la lecture du code.
la source
Cela dépend du contexte. Si c'est un jeu iPhone occasionnel, probablement pas. Le système de contrôle de vol pour le prochain véhicule spatial habité, probablement. Tout dépend des conséquences si le «mauvais» résultat se produit, mesurées par rapport au coût estimé pour le réparer.
Il existe rarement une réponse «unique» pour ce type de questions, car il ne s'agit pas de questions de programmation, mais de questions d'économie.
la source
Oui, attendez-vous à l'inattendu. J'ai passé des heures (dans le code des autres peuples ^^) à rechercher des conditions qui ne devraient jamais se produire.
Des choses telles que toujours avoir un autre, toujours avoir un cas par défaut, initialiser des variables (oui, vraiment .. des bugs surviennent à partir de cela), vérifier vos boucles pour des variables réutilisées à chaque itération, etc.
Si vous êtes particulièrement préoccupé par les problèmes de filetage, lisez des blogs, des articles et des livres sur le sujet. Le thème actuel semble être des données immuables.
la source
Juste le réparer.
J'ai vu exactement ceci. Un thread parvient à envoyer une requête réseau à un serveur qui effectue une recherche complexe dans la base de données et répond avant que l'autre thread n'entre dans la ligne de code suivante. Ça arrive.
Un client, quelque part, décidera un jour d'exécuter quelque chose qui absorbe tout le temps de calcul du thread "rapide" tout en laissant le thread lent en marche, et vous serez désolé :)
la source
Si vous avez reconnu une condition de concurrence improbable, documentez-la au moins dans le code!
EDIT: Je devrais ajouter que je corrigerais ce problème dans la mesure du possible, mais au moment de la rédaction de ce qui précède, aucune autre réponse ne dit explicitement au moins documenter le problème dans le code.
la source
Je pense que si vous savez déjà comment et pourquoi cela pourrait arriver, vous pouvez aussi vous en occuper. C’est-à-dire si cela ne prend pas beaucoup de ressources.
la source
Tout dépend des conséquences d'une situation de concurrence critique. Je pense que les personnes qui ont répondu à votre question sont correctes pour leur travail. Le mien est des moteurs de configuration de routeur. Pour moi, les conditions de concurrence rendent les systèmes immobiles, corrompus ou non configurés, même s’ils disaient que c’était une réussite. J'utilise toujours des sémaphores par routeur afin de ne rien nettoyer à la main.
Je pense que certains de mes codes d'interface graphique sont toujours sujets aux conditions de concurrence, de sorte qu'un utilisateur risque de recevoir une erreur en raison d'une situation de concurrence, demande après un tel événement.
la source
Curieusement, j'ai rencontré ce problème récemment. Je n'avais même pas réalisé qu'une condition de concurrence était possible dans mon cas. La situation de concurrence critique ne s'est manifestée que lorsque les processeurs multicœurs sont devenus la norme.
Le scénario était à peu près comme ça. Un pilote de périphérique a déclenché des événements que le logiciel doit gérer. Le contrôle devait revenir au pilote de périphérique dès que possible pour éviter un dépassement du délai d'attente sur le périphérique. Pour ce faire, l'événement a été enregistré et mis en file d'attente dans un thread distinct.
Cela a bien fonctionné pendant des années. Puis, soudainement, cela échouerait dans certaines configurations. Il s'avère que le thread de mise en file d'attente s'exécutait désormais réellement en parallèle du thread de gestion des événements, au lieu de partager le temps d'un seul processeur. Il a réussi à envoyer la commande suivante au périphérique avant que l'événement ait été acquitté, ce qui a provoqué une erreur de séquence.
Etant donné que cela ne concernait qu'un client dans une configuration, j'ai honteusement indiqué
Thread.Sleep(1000)
le problème. Il n'y a pas eu de problème depuis.la source