web-dev-qa-db-fra.com

Dois-je m'occuper de conditions de course qui n'ont presque certainement aucune chance de se produire?

Considérons quelque chose comme une application GUI où le thread principal met à jour l'interface utilisateur presque instantanément, et un autre thread interroge les données sur le réseau ou quelque chose qui est garanti pour prendre 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 condition de concurrence d'une impossibilité statistique, ne vous inquiétez pas du tout, mais d'autres ont dit que s'il y avait même un 10-53% (Je ne vous moque pas des chiffres, c'est ce que j'ai entendu) de la magie vaudou qui se produit en raison de la condition de concurrence, obtenez/libérez toujours des verrous sur le thread qui en a besoin.

Quelles sont vos pensées? Est-ce une bonne pratique de programmation de gérer les conditions de concurrence dans de telles situations statistiquement impossibles? ou serait-il totalement inutile ou même contre-productif d'ajouter plus de lignes de code pour nuire à la lisibilité?

52
l46kok

S'il s'agit vraiment d'un événement 1 sur 10 ^ 55, il ne serait pas nécessaire de le coder. Cela impliquerait que si vous faisiez l'opération 1 million de fois par seconde, vous obtiendrez un bug tous les 3 * 10 ^ 41 ans, ce qui correspond à peu près à 10 ^ 31 fois l'âge de l'univers. Si votre application ne contient une erreur qu'une seule fois pour chaque billion de milliards de milliards d'âges de l'univers, c'est probablement assez fiable.

Cependant, je parierais très fortement 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 occasionnellement, ce qui vaut la peine de coder correctement pour commencer. De plus, si vous codez les threads correctement au début afin qu'ils obtiennent et libèrent les verrous de manière appropriée, le code sera beaucoup plus facile à gérer à l'avenir. Vous n'avez pas à vous inquiéter lorsque vous effectuez un changement: vous devez ré-analyser toutes les conditions de course potentielles, recalculer leurs probabilités et vous assurer qu'elles ne se reproduiront pas.

138
Justin Cave

Du point de vue coût-avantage, vous ne devez écrire du code supplémentaire que s'il vous procure suffisamment d'avantages.

Par exemple, si la pire chose qui se produirait si un mauvais fil "gagne la course" est que les informations ne s'afficheraient pas et que l'utilisateur devrait cliquer sur "rafraîchir", ne vous embêtez pas à vous prémunir contre la condition de concurrence: devoir écrire beaucoup de code ne vaut pas la peine de réparer quelque chose d'aussi insignifiant.

D'un autre côté, si la condition de concurrence peut entraîner des transferts d'argent incorrects entre les comptes bancaires, vous devez vous prémunir contre la condition de concurrence quel que soit le code à écrire pour résoudre ce problème.

70
dasblinkenlight

Trouver une condition de concurrence est la partie difficile. Vous avez probablement passé presque autant de temps à rédiger cette question qu'il vous aurait fallu pour la corriger. Ce n'est pas comme si cela le rendait beaucoup moins lisible. Les programmeurs attendent pour voir le code de synchronisation dans de telles situations, et pourraient en fait perdre plus temps à se demander pourquoi il n'est pas là et si l'ajouter résoudrait leur bogue indépendant.

En ce qui concerne les probabilités, vous seriez surpris. L'an dernier, j'avais un rapport de bogue sur les conditions de concurrence que je ne pouvais pas reproduire avec des milliers d'essais automatisés, mais n système de n client le voyait tout le temps. La valeur commerciale de consacrer 5 minutes à le corriger maintenant, par rapport à la possibilité de dépanner un bogue "impossible" lors de l'installation d'un client, rend le choix évident.

45
Karl Bielefeldt

Obtenez et libérez les verrous. Les probabilités changent, les algorithmes changent. C'est une mauvaise habitude à prendre, et quand quelque chose ne va pas, vous n'avez pas à vous arrêter et à vous demander si vous avez mal tourné ...

27
jmoreno

et un autre thread interroge les données sur le réseau ou quelque chose qui est garanti pour prendre 5-10 secondes pour terminer le travail.

Jusqu'à ce que quelqu'un introduise une couche de mise en cache pour améliorer les performances. Soudain, cette autre bande de roulement a terminé presque instantanément et la condition de course se manifeste le plus souvent.

Si exactement cela s'était produit il y a quelques semaines, il a fallu environ 2 jours complets aux développeurs pour trouver le bogue.

Toujours fixez les conditions de course si vous les reconnaissez.

13
Michael Borgwardt

Simple vs correct.

Dans de nombreux cas, la simplicité l'emporte sur l'exactitude. C'est un problème 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 rapport fasse que votre condition de course se produise soudainement la moitié du temps. Sauf si vous activez les journaux ou déboguez le code bien sûr.

Une alternative pragmatique à la prévention d'une condition de concurrence (qui peut être délicate) peut être de la détecter et de la journaliser (bonus pour échec difficile et précoce). Si cela n'arrive 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.

8
ptyx

Si votre condition de concurrence est liée à la sécurité, vous devez 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 conduire à des attaques par escalade de privilèges si le programme avec la condition de concurrence s'exécute avec des privilèges plus élevés que l'utilisateur interagissant avec lui, comme un processus de démon système ou pire encore, le noyau.

Même si une condition de course a quelque chose comme 10 ^ (- 80) chances de se produire au hasard, il se pourrait bien qu'un attaquant déterminé ait une chance décente de créer de telles conditions délibérément et artificiellement.

7
Bristol

Therac-25!

Les développeurs du projet Therac-25 étaient assez confiants quant au délai entre une interface utilisateur et un problème lié à l'interface dans une machine XRAY thérapeutique.

Ils n'auraient pas dû l'être.

Vous pouvez en savoir plus sur cette fameuse catastrophe logicielle de la vie ou de la mort sur:

http://www.youtube.com/watch?v=izGSOsAGIVQ

ou

http://en.wikipedia.org/wiki/Therac-25

Votre application peut être beaucoup moins sensible aux pannes que les appareils médicaux. Une méthode utile consiste à classer l'exposition au risque comme le produit de la probabilité d'occurrence et du coût d'occurrence sur la durée de vie du produit pour toutes les unités qui pourraient être produites.

Si vous avez choisi de construire votre code pour durer (et cela semble être le cas), vous devriez considérer la loi de Moore qui peut facilement éliminer 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 de copies, éliminez plus de zéros. Si les utilisateurs effectuent cette opération quotidiennement (ou mensuellement) pendant des années, retirez-en quelques-uns de plus. S'il est utilisé là où la fibre Google est disponible, que faire alors? Si les déchets de l'interface utilisateur collectent le fonctionnement de l'interface graphique, 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 calendrier?

Les sémaphores, les verrous, les mutex, la synchronisation des barrières sont parmi les moyens de synchroniser les activités entre les threads. Potentiellement, si vous ne les utilisez pas, une autre personne qui gère votre programme peut changer, puis assez rapidement, les hypothèses sur les relations entre les threads et le calcul de la condition de concurrence peut être invalidé.

Je vous recommande de synchroniser explicitement, car même si vous ne le voyez peut-être jamais créer un problème, un client peut le faire. De plus, même si votre condition de course ne se produit jamais, que se passe-t-il si vous ou votre organisation êtes appelés au tribunal pour défendre votre code (car Toyota était lié à 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 prémunissons contre ce cas improbable comme celui-ci ..." que de dire: "nous savons que notre code échouera, mais nous avons noté cette équation pour montrer que cela ne se produira pas de notre vivant. Probablement. "

On dirait que le calcul de probabilité vient de quelqu'un d'autre. Connaissent-ils votre code et les connaissez-vous suffisamment pour croire qu'aucune erreur n'a été commise? Si j'ai calculé une fiabilité de 99,99997% pour quelque chose, je pourrais aussi penser à mes cours de statistiques universitaires et me rappeler que je n'ai pas toujours obtenu 100%, et reculer de quelques pour cent sur mes propres estimations de fiabilité personnelles.

6
DeveloperDon

serait-il totalement inutile ou même contre-productif d'ajouter plus de lignes de code pour nuire à la lisibilité?

La simplicité n'est bonne que lorsqu'elle est également correcte. Comme ce code n'est pas correct, les futurs programmeurs le feront le regarderont inévitablement lorsqu'ils chercheront un bogue connexe.

Quelle que soit la façon dont vous le gérez (soit en le journalisant, en le documentant, soit en ajoutant les verrous - cela dépend du coût), vous économiserez du temps aux autres programmeurs en regardant le code.

4
Casey Kuball

Oui, attendez-vous à l'inattendu. J'ai passé des heures (dans le code d'autres peuples ^^) à rechercher des conditions qui ne devraient jamais se produire.

Des choses comme avoir toujours un autre, toujours avoir un cas par défaut, initialiser les variables (oui, vraiment .. des bugs se produisent à partir de cela), vérifier vos boucles pour les variables réutilisées pour chaque itération, etc.

Si vous êtes particulièrement préoccupé par les problèmes de threading, lisez des blogs, des articles et des livres sur le sujet. Le thème actuel semble être des données immuables.

3
Paul

Il suffit de le réparer.

J'ai vu exactement ça. Un thread parvient à faire une requête réseau à un serveur qui effectue une recherche de base de données complexe et répond avant que l'autre thread n'ait atteint la ligne de code suivante. Ça arrive.

Un client quelque part décidera un jour d'exécuter quelque chose qui monopolise tout le temps CPU pour le thread "rapide" tout en laissant le thread lent fonctionner, et vous serez désolé :)

3
JohnB

Cela dépendrait du contexte. Si c'est un jeu iPhone décontracté, probablement pas. Le système de commande de vol du prochain véhicule spatial habité, probablement. Tout dépend des conséquences si le "mauvais" résultat est mesuré par rapport au coût estimé de sa réparation.

Il y a rarement une réponse unique pour ces types de questions car ce sont des questions de programmation pas, mais plutôt des questions économiques.

3
GrandmasterB

Si vous avez reconnu une condition de course improbable, documentez-la au moins dans le code!

EDIT: Je dois ajouter que je le corrigerais si possible, mais au moment d'écrire ce qui précède, aucune autre réponse n'a explicitement dit au moins documenter le problème dans le code.

1
Mark Hurd

Je pense que si vous savez déjà comment et pourquoi cela pourrait arriver, vous pourriez tout aussi bien y faire face. C'est-à-dire si cela ne prend pas une quantité abondante de ressources.

0
Sjaak van der Heide

Tout dépend des conséquences d'une condition de concurrence. Je pense que les personnes répondant à votre question sont correctes pour leur ligne de 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 ont dit que cela avait réussi. J'utilise toujours des sémaphores par routeur pour ne pas avoir à nettoyer quoi que ce soit à la main.

Je pense qu'une partie de mon code GUI est toujours sujette aux conditions de concurrence de telle manière qu'un utilisateur peut recevoir une erreur car une situation de concurrence s'est produite, mais je n'aurais pas de telles possibilités s'il y a un risque de corruption de données ou de mauvaise conduite du demande après un tel événement.

0
Sylwester

Curieusement, j'ai rencontré ce problème récemment. Je ne savais même pas qu'une condition de course était possible dans ma situation. La condition de concurrence ne s'est présenté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 a dû retourner au pilote de périphérique dès que possible pour éviter un délai d'attente sur le périphérique. Pour garantir cela, l'événement a été enregistré et mis en file d'attente dans un thread distinct.

Receive event from device:
{
    Record event details.
    Enqueue event in the queuing thread.
    Acknowledge the event.
}

Queueing thread receives an event:
{
    Retrieve event details.
    Process event.
    Send next command to device.
}

Cela a bien fonctionné pendant des années. Puis soudain, il échouerait dans certaines configurations. Il s'avère que le thread de mise en file d'attente s'exécutait désormais en parallèle avec le thread de gestion des événements, plutôt que 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 n'ait été reconnu, provoquant une erreur hors séquence.

Étant donné que cela n'a affecté qu'un seul client dans une configuration, j'ai honteusement mis une Thread.Sleep(1000) à l'endroit où se trouvait le problème. Il n'y a pas eu de problème depuis.

0
Hand-E-Food