Composants thread-safe
Le partage de ressources entre des threads est souvent nécessaire dans la programmation multithread. Par exemple, plusieurs threads peuvent avoir besoin d'accéder à une base de données partagée ou mettre à jour un ensemble de variables système. La concurrence simultanée de plusieurs threads pour accéder à des ressources partagées entraîne un risque de condition de concurrence critique. Une condition de concurrence critique a lieu lorsqu'un thread modifie une ressource et rend son état non valide, puis qu'un autre thread tente d'accéder à cette ressource et de l'utiliser dans cet état. Prenons l'exemple suivant :
Public Class WidgetManipulator
Public TotalWidgets as Integer = 0
Public Sub AddWidget()
TotalWidgets += 1
Console.WriteLine("Total widgets = " & TotalWidgets.ToString)
End Sub
Public Sub RemoveWidgets()
TotalWidgets -= 10
End Sub
End Class
public class WidgetManipulator
{
public int TotalWidgets = 0;
public void AddWidget()
{
TotalWidgets++;
Console.WriteLine("Total widgets = " + TotalWidgets.ToString());
}
public void RemoveWidgets()
{
TotalWidgets -= 10;
}
}
Cette classe expose deux méthodes. La première méthode, AddWidget, ajoute 1 au champ TotalWidgets et écrit la valeur dans la console. La seconde méthode soustrait 10 de la valeur de TotalWidgets. Réfléchissez à ce qui se passerait si deux threads tentaient d'accéder en même temps à la même instance de la classe WidgetManipulator. Un thread peut appeler AddWidget en même temps que le deuxième thread appelle RemoveWidgets. Dans ce cas, la valeur de TotalWidgets pourrait être modifiée par le second thread avant que le premier thread n'indique une valeur exacte. Cette condition de concurrence critique peut entraîner des résultats inexacts et provoquer l'altération des données.
Prévention des conditions de concurrence critique à l'aide de verrous
Vous pouvez protéger des sections critiques de votre code des conditions de concurrence critique en utilisant des verrous. Un verrou, représenté par le mot clé Visual Basic instruction SyncLock ou le mot clé C# instruction lock, permet à un seul thread d'exécution d'obtenir des droits d'exécution exclusifs sur un objet. L'exemple suivant montre comment utiliser les verrous :
SyncLock MyObject
' Insert code that affects MyObject.
End SyncLock
lock(MyObject)
{
// Insert code that affects MyObject.
}
En présence d'un verrou, l'exécution sur l'objet spécifié (MyObject dans l'exemple précédent) est bloquée jusqu'à ce que le thread ait un accès exclusif à l'objet. À la fin du verrou, celui-ci est libéré et l'exécution se poursuit normalement. Vous ne pouvez obtenir de verrou que sur un objet retournant une référence. Un type valeur ne peut pas être verrouillé de cette manière.
Inconvénients des verrous
L'utilisation de verrous permet de garantir que plusieurs threads n'accèdent pas à un objet en même temps. Ils peuvent cependant entraîner une dégradation significative des performances. Prenons l'exemple d'un programme dans lequel plusieurs threads sont exécutés. Si chaque thread a besoin d'utiliser un objet particulier et doit attendre l'obtention d'un verrou exclusif sur cet objet pour s'exécuter, les threads interrompront tous leur exécution et seront sauvegardés les uns après les autres, dégradant ainsi les performances. C'est pourquoi vous ne devez utiliser de verrous que lorsque vous avez du code qui doit être exécuté comme une unité. Vous pouvez par exemple mettre à jour plusieurs ressources interdépendantes. Ce code est appelé atomique. En limitant vos verrous au code à exécuter de façon atomique, vous pourrez écrire des composants multithread garantissant la sécurité de vos données tout en conservant un bon niveau de performances.
En outre, soyez prudent afin d'éviter des situations où des interblocages risqueraient de survenir. Dans ce cas, plusieurs threads s'attendent mutuellement afin de libérer les ressources partagées. Par exemple, le Thread 1 peut détenir un verrou sur la ressource A et attendre la ressource B. Le thread 2, en revanche, peut détenir un verrou sur la ressource B et attendre la ressource A. Dans ce cas, aucun thread ne sera autorisé à continuer. La seule façon d'éviter un interblocage consiste à faire preuve de prudence lors de la programmation.
Voir aussi
Tâches
Comment : coordonner plusieurs threads d'exécution
Comment : manipuler des contrôles à partir de threads
Procédure pas à pas : création d'un composant simple multithread avec Visual Basic
Procédure pas à pas : création d'un composant simple multithread à l'aide de Visual C#
Référence
Concepts
Vue d'ensemble du modèle asynchrone basé sur des événements