Share via


Composants thread-safe

Mise à jour : novembre 2007

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 blocages risqueraient de survenir. Dans ce cas, plusieurs threads s'attendent mutuellement afin de libérer les ressources partagées. Par exemple, Thread 1 peut détenir un verrou sur la ressource A et attendre la ressource B. Thread 2 peut de son côté détenir un verrou sur la ressource B et attendre la ressource A. Dans un tel scénario, aucun des deux threads n'est autorisé à continuer. La seule façon d'éviter un blocage 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#

Concepts

Vue d'ensemble du modèle asynchrone basé sur des événements

Référence

BackgroundWorker

Autres ressources

Multithreading dans les composants