20 octobre 2006

Section critique à la bonne place, avec le bon nom...

Dans un programme utilisant plusieurs thread (messagerie instantanée avec Socket, traitements batch...), il peut arriver après un certain temps d'utilisation un blocage (pas de réponse...), ou une disparition pure et simple de la liste des processus, ou un message du mécanisme de sécurité WLangage :

Une erreur système inattendue est survenue.
Module : WD100VM.DLL
Erreur système : Access violation (GPF)
EIP = 12345678


C'est une difficulté sérieuse, car il n'y a en général aucune reproduction systématique, et le traitement donné dans la pile de l'erreur change d'une exécution à l'autre. La piste de recherche à privilégier dans pareil cas, se nomme "section critique". En effet, dès que plusieurs threads s'exécutent en parallèle, ils vont nécessairement à un moment ou à un autre accéder à des éléments partagés. Typiquement, une variable globale du projet, un membre de classe utilisé dans une méthode exécutée par un thread secondaire, et une autre partie de la classe sollicitée dans le thread principal ... les combinaisons sont infinies.

Il faut donc en tout premier lieu en cas d'instabilité vérifier que tous les éléments (variables, membres...) qui sont utilisés par deux thread distincts sont dans une section critique. Sans cela, l'erreur ou le blocage "aléatoire" est quasiment garanti à un moment ou à un autre dans le déroulement de l'application, et le plus souvent après un important nombre d'heures d'utilisation.

A titre d'exemple, si une variable globale du projet est affectée dans un thread secondaire, le code devra être de la forme suivante :

Et bien sûr, la consultation de cette variable dans le thread principal devra se faire avec la même section critique (attention à ne pas faire une faute de frappe dans le nom de la section) :

Ce billet est motivé par plusieurs instabilités aléatoires résolues en ajoutant les bonnes sections critiques. La dernière en date, hier, provenait d'une faute de frappe avec un nom de section critique erroné lors de l'affectation d'un membre !

5 commentaires:

Anonyme a dit…

Bonjour,

Il est clair qu'il est nécessaire de gérer la modification/suppression d'une ressource entre plusieurs threads.

Mais pourquoi affirmez-vous qu'il est nécessaire de synchroniser la LECTURE d'une variable???

Je suis un développeur venant du monde java, j'ai vu beaucoup de choses étonnantes en WD mais tout de même, si vous avez raison c'est que WD est vraiment pas au point sur ce plan.

Elian Lacroix a dit…

Il ne s'agit pas de toutes les variables, mais de celles qui sont partagées par différents threads. Si un thread doit écrire dans une variables globale du projet par exemple, et que cette même variable doit également être mise à jour dans un autre thread, les accès à cette variable doivent se faire dans une section critique. J'ai une expérience moindre dans les langages plus complexes, je ne saurais pas vous indiquer s'il y a un parallèle avec Java. En tout cas ça fonctionne (très bien) ainsi en WLangage.

Anonyme a dit…

En effet je pense que
Le problème de la modification simultanée se défend, en fait pour ma part j'aurai simplement créer une classe avec des Getter/Setter synchronisés pour gérer le cas de la double modification.

Ce qui m'inquiète déjà plus, c'est la lecture.
Votre raisonnement est-il le même si nous supposons qu'une variable (d'une portée globale au projet ou équivalente) soit initialisée une seule fois par le thread principal au démarrage de l'application, puis ensuite LUE par des threads secondaires?
Pensez-vous qu'il soit nécessaire de synchroniser l'accès à cette variable dans ce cas précis ou toute modification est exclue, qu'il ne s'agit que de lecture?

J'ai écrit une application serveur socket un jour, supposée fonctionner de façon permanente. Elle finissait toujours par se bloquer inexplicablement au bout de quelques jours de travail sans raison apparente, parfois avec des messages d'erreurs insensés comme celui que vous avez mentionné, d'autres fois, toutes les connexions des clients finissaient en timeout ou se perdaient irrécupérablement. Lorsque j'ai remplacé les sockets par des messages windows (WM_COPYDATA) pour les communications, et par conséquent supprimé toute la gestion des threads, les problèmes ont disparus et l'application peut tourner à présent des mois sans reboot.

C'est pour ça que cette publication sur les threads m'intéresse tant, car je suis toujours ouvert à une explication.

Elian Lacroix a dit…

La consultation seule si aucune affectation n'est faite en parallèle ne nécessite pas l'utilisation d'une section critique.

Anonyme a dit…

Bonjour, j'ai la même erreur de violation d'accès alors que je n'utilise pas de thread ou de socket, il est survenu dans un traitement long (une boucle de mise à jour sur un fichier HF)