Synchronisation des données pour le multithreading
Lorsque plusieurs threads peuvent appeler les propriétés et méthodes d’un objet unique, il est essentiel que ces appels soient synchronisés. Sinon, un thread peut interrompre l’action d’un autre thread, et l’objet peut être laissé dans un état non valide. Une classe dont les membres sont protégés de ces interruptions est appelée « thread-safe ».
Le .NET fournit plusieurs stratégies pour synchroniser l’accès aux membres statiques et d’instance :
Régions de code synchronisées. Vous pouvez utiliser la classe Monitor ou la prise en charge du compilateur pour cette classe afin de synchroniser uniquement le bloc de code qui en a besoin, ce qui améliore les performances.
Synchronisation manuelle. Vous pouvez utiliser les objets de synchronisation fournis par la bibliothèque de classes .NET. Voir Vue d’ensemble des primitives de synchronisation, qui inclut également une présentation de la classe Monitor.
Contextes synchronisés. Pour les applications .NET Framework et Xamarin uniquement, vous pouvez utiliser SynchronizationAttribute pour activer une synchronisation simple et automatique des objets ContextBoundObject.
Classes de collection dans l’espace de noms System.Collections.Concurrent. Ces classes fournissent des opérations d’ajout et de suppression intégrées et synchronisées. Pour plus d’informations, consultez Collections thread-safe.
Le common language runtime fournit un modèle de thread dans lequel les classes se répartissent en plusieurs catégories qui peuvent être synchronisées de différentes manières, selon les besoins. Le tableau suivant indique le type de prise en charge de la synchronisation fourni pour les champs et méthodes dans une catégorie de synchronisation donnée.
Category | Champs globaux | Champs statiques | Méthodes statiques | Champs d'instance | Méthodes d’instance | Blocs de code spécifiques |
---|---|---|---|---|---|---|
Aucune synchronisation | Non | Non | Non | Non | Non | Non |
Contexte synchronisé | Non | Non | Non | Oui | Oui | Non |
Régions de code synchronisées | Non | Non | Seulement en cas de marquage | Non | Seulement en cas de marquage | Seulement en cas de marquage |
Synchronisation manuelle | Manuel | Manuel | Manuel | Manuel | Manuel | Manuel |
Aucune synchronisation
Il s’agit de la valeur par défaut pour les objets. N’importe quel thread peut accéder à toute méthode ou champ et ce, à tout moment. Par contre, un seul thread à la fois doit accéder à ces objets.
Synchronisation manuelle
La bibliothèque de classes .NET fournit plusieurs classes pour la synchronisation des threads. Voir Vue d’ensemble des primitives de synchronisation.
Régions de code synchronisées
Vous pouvez utiliser la classe Monitor ou un mot-clé de compilateur pour synchroniser des blocs de code ainsi que des méthodes d’instance et statiques. Les champs statiques synchronisés ne sont pas pris en charge.
Visual Basic et C# prennent en charge le marquage de blocs de code avec un mot-clé d’un langage spécifique, l’instruction lock
en C# ou l’instruction SyncLock
en Visual Basic. Lorsque le code est exécuté par un thread, une tentative d’acquisition du verrou est effectuée. Si le verrou a déjà été acquis par un autre thread, le thread se bloque jusqu’à ce qu’il devienne disponible. Lorsque le thread quitte le bloc de code synchronisé (d’une manière ou d’une autre), le verrou est libéré.
Remarque
À compter de C# 13, l’instruction lock
reconnaît si l’objet verrouillé est une instance de System.Threading.Lock et utilise la méthode EnterScope
pour créer une région synchronisée. Le lock
, quant la cible n’est pas une instance Lock
et que les instructions SyncLock
sont implémentées en utilisant Monitor.Enter et Monitor.Exit. D’autres méthodes de Monitor peuvent donc être utilisées conjointement dans la région synchronisée.
Vous pouvez également décorer une méthode avec un MethodImplAttribute ayant la valeur MethodImplOptions.Synchronized, ce qui a le même effet que l’utilisation de Monitor ou de l’un des mots clés de compilateur pour verrouiller l’intégralité du corps de la méthode.
Vous pouvez utiliser Thread.Interrupt pour supprimer le blocage appliqué par un thread à des opérations comme l’attente de l’accès à une région de code synchronisée. Thread.Interrupt est également utilisé pour libérer des threads d’opérations telles que Thread.Sleep.
Important
Ne verrouillez pas le type (typeof(MyType)
en C#, GetType(MyType)
en Visual Basic, ou MyType::typeid
en C++) pour protéger les méthodes static
(méthodes Shared
dans Visual Basic). Utilisez plutôt un objet statique privé. De même, n’utilisez pas this
en C# (Me
en Visual Basic) pour verrouiller des méthodes d’instance. Utilisez plutôt un objet privé. Une classe ou une instance peut être verrouillée par un code autre que le vôtre, ce qui peut entraîner des blocages ou des problèmes de performances.
Prise en charge du compilateur
Visual Basic et C# prennent en charge un mot clé de langage qui utilise Monitor.Enter et Monitor.Exit pour verrouiller l’objet. Visual Basic prend en charge l’instruction SyncLock et C#, l’instruction lock.
Dans les deux cas, si une exception est déclenchée dans le bloc de code, le verrou acquis par l’instruction lock ou SyncLock est automatiquement libéré. Les compilateurs C# et Visual Basic émettent un bloc try/finally avec Monitor.Enter au début de la tentative, et Monitor.Exit dans le bloc finally. Si une exception est déclenchée dans le bloc lock ou SyncLock, le gestionnaire finally s’exécute pour vous permettre d’effectuer d’éventuelles tâches de nettoyage.
Contexte synchronisé
Dans les applications .NET Framework et Xamarin uniquement, vous pouvez utiliser SynchronizationAttribute sur n’importe quel ContextBoundObject pour synchroniser l’ensemble des champs et méthodes d’instance. Tous les objets d’un même domaine de contexte partagent le même verrou. Plusieurs threads sont autorisés à accéder aux méthodes et champs, mais un seul thread est autorisé à la fois.