Notes
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Visual Basic .NET ou Visual Basic offre la possibilité d’utiliser des threads dans les applications Visual Basic pour la première fois. Les threads introduisent des problèmes de débogage tels que les conditions de concurrence et les blocages. Cet article explore ces deux problèmes.
Version de produit d’origine : Visual Basic, Visual Basic .NET
Numéro de base de connaissances d’origine : 317723
Lorsque des conditions de concurrence se produisent
Une condition de concurrence se produit lorsque deux threads accèdent à une variable partagée en même temps. Le premier thread lit la variable et le deuxième thread lit la même valeur de la variable. Ensuite, le premier thread et le deuxième thread effectuent leurs opérations sur la valeur, et ils effectuent une course pour voir quel thread peut écrire la valeur en dernier dans la variable partagée. La valeur du thread qui écrit sa valeur en dernier est conservée, car le thread écrit sur la valeur que le thread précédent a écrite.
Détails et exemples d’une condition de course
Chaque thread est alloué à une période prédéfinie pour s’exécuter sur un processeur. Lorsque l’heure d’expiration du thread est allouée, le contexte du thread est enregistré jusqu’à son prochain activation du processeur et le processeur commence l’exécution du thread suivant.
Comment une commande à une ligne peut-elle entraîner une condition de concurrence
Examinez l’exemple suivant pour voir comment une condition de concurrence se produit. Il existe deux threads et les deux mettent à jour une variable partagée appelée total (qui est représentée comme dword ptr ds:[031B49DCh]
dans le code d’assembly).
Thread 1
Total = Total + val1
Thread 2
Total = Total - val2
Code d’assembly (avec numéros de ligne) à partir de la compilation du code Visual Basic précédent :
Thread 1
1. mov eax,dword ptr ds:[031B49DCh] 2. add eax,edi 3. jno 00000033 4. xor ecx,ecx 5. call 7611097F 6. mov dword ptr ds:[031B49DCh],eax
Thread 2
1. mov eax,dword ptr ds:[031B49DCh] 2. sub eax,edi 3. jno 00000033 4. xor ecx,ecx 5. call 76110BE7 6. mov dword ptr ds:[031B49DCh],eax
En examinant le code d’assembly, vous pouvez voir le nombre d’opérations effectuées par le processeur au niveau inférieur pour exécuter un calcul d’ajout simple. Un thread peut être en mesure d’exécuter tout ou partie de son code d’assembly pendant son temps sur le processeur. Regardez maintenant comment une condition de race se produit à partir de ce code.
Total
est 100, val1
est 50 et val2
est 15. Le thread 1 peut s’exécuter, mais effectue uniquement les étapes 1 à 3. Cela signifie que le thread 1 lit la variable et termine l’ajout. Le thread 1 est maintenant en attente d’écrire sa nouvelle valeur de 150. Une fois le thread 1 arrêté, thread 2 est complètement exécuté. Cela signifie qu’il a écrit la valeur calculée (85) dans la variable Total
. Enfin, le thread 1 récupère le contrôle et termine l’exécution. Il écrit sa valeur (150). Par conséquent, lorsque le thread 1 est terminé, la valeur est Total
maintenant 150 au lieu de 85.
Vous pouvez voir comment cela peut être un problème majeur. S’il s’agit d’un programme bancaire, le client aurait de l’argent dans son compte qui ne devrait pas être présent.
Cette erreur est aléatoire, car il est possible que thread 1 termine son exécution avant l’expiration du processeur, puis thread 2 peut commencer son exécution. Si ces événements se produisent, le problème ne se produit pas. L’exécution de threads n’est pas déterministe, par conséquent, vous ne pouvez pas contrôler le temps ou l’ordre d’exécution. Notez également que les threads peuvent s’exécuter différemment en mode runtime et débogage. En outre, vous pouvez voir que si vous exécutez chaque thread dans la série, l’erreur ne se produit pas. Cette aléatoire rend ces erreurs beaucoup plus difficiles à suivre et à déboguer.
Pour empêcher les conditions de concurrence de se produire, vous pouvez verrouiller les variables partagées, afin qu’un seul thread à la fois ait accès à la variable partagée. Faites cela avec parcimonie, car si une variable est verrouillée dans Thread 1 et Thread 2 a également besoin de la variable, l’exécution de Thread 2 s’arrête pendant que Thread 2 attend que Thread 1 libère la variable. (Pour plus d’informations, consultez SyncLock
la section Références de cet article.)
Symptômes d’une condition de race
Le symptôme le plus courant d’une condition de concurrence est des valeurs imprévisibles de variables partagées entre plusieurs threads. Cela résulte de l’inédictibilité de l’ordre dans lequel les threads s’exécutent. Parfois, un thread gagne, et parfois l’autre thread gagne. À d’autres moments, l’exécution fonctionne correctement. En outre, si chaque thread est exécuté séparément, la valeur de la variable se comporte correctement.
Lorsque des interblocages se produisent
Un interblocage se produit lorsque deux threads verrouillent chaque variable différente en même temps, puis essaient de verrouiller la variable que l’autre thread a déjà verrouillée. Par conséquent, chaque thread cesse d’exécuter et attend que l’autre thread libère la variable. Étant donné que chaque thread contient la variable souhaitée par l’autre thread, rien ne se produit et les threads restent bloqués.
Détails et exemples pour les interblocages
Le code suivant comporte deux objets et LeftVal
RightVal
:
Thread 1
SyncLock LeftVal SyncLock RightVal 'Perform operations on LeftVal and RightVal that require read and write. End SyncLock End SyncLock
Thread 2
SyncLock RightVal SyncLock LeftVal 'Perform operations on RightVal and LeftVal that require read and write. End SyncLock End SyncLock
Un interblocage se produit lorsque le thread 1 est autorisé à verrouiller LeftVal
. Le processeur arrête l’exécution du thread 1 et commence l’exécution de Thread 2. Thread 2 verrous RightVal
, puis tente de verrouiller LeftVal
. Comme LeftVal
verrouillé, thread 2 s’arrête et attend la LeftVal
publication. Étant donné que thread 2 est arrêté, thread 1 est autorisé à continuer à s’exécuter. Le thread 1 tente de verrouiller RightVal
, mais ne peut pas, car le thread 2 l’a verrouillé. Par conséquent, le thread 1 commence à attendre que RightVal devienne disponible. Chaque thread attend l’autre thread, car chaque thread a verrouillé la variable sur laquelle l’autre thread attend, et aucun thread ne déverrouille la variable qu’il contient.
Un blocage ne se produit pas toujours. Si le thread 1 exécute les deux verrous avant que le processeur ne l’arrête, thread 1 peut effectuer ses opérations, puis déverrouiller la variable partagée. Une fois que thread 1 déverrouille la variable, thread 2 peut poursuivre son exécution, comme prévu.
Cette erreur semble évidente lorsque ces extraits de code sont placés côte à côte, mais dans la pratique, le code peut apparaître dans des modules ou des zones distincts de votre code. Il s’agit d’une erreur difficile à suivre, car, à partir de ce même code, l’exécution correcte et l’exécution incorrecte peuvent se produire.
Symptômes des interblocages
Un symptôme courant d’interblocage est que le programme ou le groupe de threads cesse de répondre. C’est également ce qu’on appelle un blocage. Au moins deux threads attendent une variable verrouillée par l’autre thread. Les threads ne continuent pas, car aucun thread ne libère sa variable tant qu’elle n’obtient pas l’autre variable. L’ensemble du programme peut se bloquer si le programme attend un ou les deux threads pour terminer l’exécution.
Qu’est-ce qu’un thread ?
Les processus sont utilisés pour séparer les différentes applications qui s’exécutent à une heure spécifiée sur un seul ordinateur. Le système d’exploitation n’exécute pas de processus, mais les threads le font. Un thread est une unité d’exécution. Le système d’exploitation alloue du temps processeur à un thread pour l’exécution des tâches du thread. Un seul processus peut contenir plusieurs threads d’exécution. Chaque thread gère ses propres gestionnaires d’exceptions, ses priorités de planification et un ensemble de structures utilisées par le système d’exploitation pour enregistrer le contexte du thread si le thread ne peut pas terminer son exécution pendant le temps qu’il a été affecté au processeur. Le contexte est conservé jusqu’à la prochaine fois que le thread reçoit le temps processeur. Le contexte inclut toutes les informations dont le thread a besoin pour poursuivre son exécution en toute transparence. Ces informations incluent l’ensemble des registres de processeur du thread et la pile des appels à l’intérieur de l’espace d’adressage du processus hôte.
References
Pour plus d’informations, recherchez les mots clés suivants dans Visual Studio Help :
SyncLock
. Permet à un objet d’être verrouillé. Si un autre thread tente de verrouiller ce même objet, il est bloqué jusqu’à ce que le premier thread se libère. UtilisezSyncLock
attentivement, car les problèmes peuvent résulter de l’utilisation incorrecte de SyncLock. Par exemple, cette commande peut empêcher les conditions de concurrence, mais provoquer des interblocages.InterLocked
. Autorise un ensemble sélectionné d’opérations thread-safe sur des variables numériques de base.
Pour plus d’informations, consultez Threads et Threading.