Je suis en train de lutter contre une condition de concurrence potentielle dans mon application, qui repose sur l'accès multiple à un objet partagé pour déterminer quelle devrait être leur prochaine opération logique. Je n'ai certainement pas de problème à griffonner des trucs sur un morceau de papier ou sur le tableau blanc, et à l'utiliser pour tracer l'accès à cet objet partagé. Cependant, ce serait bien de le faire avec des diagrammes UML. J'utilise généralement UML mais il est logique pour moi visuellement, et non toujours le "chemin UML". J'utilise à peu près uniquement des diagrammes simples tels que l'état, la classe, l'activité et les diagrammes de communication. Je ne suis pas sûr lequel des autres disponibles est le mieux adapté à la cartographie de plusieurs threads + mémoire partagée. Quelqu'un peut-il offrir ses suggestions, et/ou des expériences avec la modélisation de ce genre de chose?Utilisation de UML pour détecter des conditions de concurrence potentielles dans la conception
Répondre
Je visualise l'ordre d'instruction en utilisant des chronologies. Considérons le code suivant.
x++;
y++;
Donc, nos instructions se décomposent comme ceci.
Read(x) --> Increment(x) --> Write(x) --> Read(y) --> Increment(y) --> Write(y)
Par souci de concision, raccourcissons les étapes suivantes. Donc, si nous avons 2 threads mettant en œuvre ces instructions, alors nous pouvons dessiner 2 timelines comme ça.
T1: R(x) --> I(x) --> W(x) --> R(y) --> I(y) --> W(y)
T2: R(x) --> I(x) --> W(x) --> R(y) --> I(y) --> W(y)
Maintenant, c'est le grand pas. Lorsque vous dessinez les chronologies possibles, faites-le dans la perspective d'un autre thread. Ceci est important car les instructions peuvent être réorganisées du point de vue d'un autre thread (avec les contraintes). Donc, le diagramme suivant est une séquence d'exécution complètement valide pour T1
comme vu d'un autre thread.
T1: R(y) --> R(x) --> I(x) --> I(y) --> W(y) --> W(x)
permet maintenant ajouter les caractères [
et ]
pour représenter verrouillage et de déverrouillage acquérir verrouillage respectivement. Un [
ne peut pas dépasser un autre [
et il ne peut pas se déplacer avant un ]
. Des contraintes de réorganisation similaires s'appliquent au caractère ]
. Il est également important qu'aucune instruction ne puisse dépasser [
ou ]
, mais elles sont toujours libres de se déplacer dans la section verrouillée. Le résultat final est que les verrous sérialisent l'exécution des instructions (vous le savez déjà) et ils empêchent le réordonnancement dans une certaine mesure (que vous n'avez peut-être pas su).
T1: [ -> R(x) ------> I(x) --> W(x) ---> ] ----------------------------------------------------------------------------> [ -----> R(y) -> I(y) --> W(y) -----> ]
T2: -------------------------------------> [ -> R(x) -----> I(x) ---> W(x) ----> ] -> [ --> R(y) --> I(y) --> W(y) -> ]
Ainsi, lorsque vous dessinez vos délais l'-->
(espacement entre les instructions) peuvent être arbitrairement ou raccourcies à rallongés simululate l'entrelacement de fils. La longueur des retards des instructions dépendra fortement du planificateur de threads, des mécanismes de synchronisation utilisés, etc. et sera généralement très aléatoire.
Expérimentez en déplaçant les instructions à gauche et à droite sur la timeline pour vous aider à visualiser ce qui pourrait se produire. Créez vos propres symboles et abréviations pour les verrous, les instructions et les retards afin d'aider à la visualisation et pour indiquer jusqu'à quel point vous pouvez déplacer chaque élément vers la gauche ou vers la droite.
En réalité, aucune lecture de la même variable ne peut dépasser une écriture. Cela devrait être intuitif, mais cela ne fait pas de mal de formaliser cette contrainte.
merci, c'est une approche intéressante. Je vais regarder dedans. – Dave