Assurer un accès Thread-Safe
aux membres de Singleton en C#
Dans de nombreuses applications C#, un modèle singleton est souvent implémenté pour garantir qu’une classe n’ait qu’une seule instance et fournisse un point d’accès global à cette instance. Cependant, lorsque plusieurs threads accèdent aux membres d’un singleton, cela soulève des préoccupations concernant la sécurité des threads. Cet article de blog se penche sur ce problème, en se concentrant spécifiquement sur un scénario courant impliquant la méthode Toggle()
d’une classe singleton et sur la façon d’assurer des opérations thread-safe à l’intérieur.
Comprendre le Problème
Considérons la classe singleton suivante en C# :
public class MyClass
{
private static readonly MyClass instance = new MyClass();
public static MyClass Instance
{
get { return instance; }
}
private int value = 0;
public int Toggle()
{
if(value == 0)
{
value = 1;
}
else if(value == 1)
{
value = 0;
}
return value;
}
}
La méthode Toggle()
ici est conçue pour basculer la value
entre 0 et 1. Cependant, si plusieurs threads invoquent Toggle()
simultanément, la méthode n’est pas thread-safe. En conséquence, cela peut entraîner un comportement imprévisible et des résultats incorrects.
Pourquoi Toggle()
n’est-il pas Thread-Safe ?
Lorsque deux threads accèdent à Toggle()
en même temps, leur exécution peut se chevaucher de telle manière que les deux threads lisent et modifient la même variable value
. Cette situation peut conduire à des scénarios où le résultat attendu ne se produit pas. Par exemple, les deux threads pourraient évaluer value
en même temps, entraînant une condition de course.
Exemple de Condition de Course
// Exécution du Thread 1
if(value == 0)
{
value = 1;
// Le Thread 2 intervient maintenant
// Le Thread 2 évalue value à 1 et le met à 0
}
// Le Thread 1 retourne 0, ce qui n'était pas la valeur attendue.
Comment Rendre Toggle()
Thread-Safe
Le Principe de Verrouillage
Pour atteindre la sécurité des threads, nous devons nous assurer que lorsqu’un thread exécute une section critique de code (dans ce cas, la modification de value
), aucun autre thread ne peut intervenir. En C#, nous pouvons implémenter cela en utilisant l’instruction lock
.
Solution étapes par étapes
-
Identifier les Sections Critiques : Déterminer quelles sections de code doivent être exécutées sans interruption. Dans ce cas, la lecture et l’écriture de
value
. -
Créer un Objet de Verrouillage : Étant donné que
value
est un type valeur (unint
), il ne peut pas être verrouillé. Par conséquent, nous avons besoin d’un objet séparé pour le verrouillage.
private static readonly object locker = new object();
- Encapsuler le Code dans un Verrou : Utilisez l’instruction
lock
pour créer une section critique pour modifier lavalue
.
Méthode Toggle Révisée
Voici à quoi ressemblera la méthode Toggle()
après la mise en œuvre de la sécurité des threads :
public int Toggle()
{
lock (locker)
{
if(value == 0)
{
value = 1;
}
else if(value == 1)
{
value = 0;
}
return value;
}
}
Points Clés à Retenir
- Mécanisme de Verrouillage : En verrouillant sur un objet dédié, vous vous assurez qu’un seul thread peut accéder à la section critique à la fois.
- Éviter de Verrouiller les Types Valeurs : Rappelez-vous toujours que vous ne pouvez verrouiller que sur des types de référence, d’où la nécessité d’un objet
locker
. - Complétude de l’Implémentation : Revoyez toujours et identifiez quelles sections de votre code peuvent être affectées par un accès concurrent pour prévenir les conditions de course.
Conclusion
Rendre votre classe singleton thread-safe est crucial dans un environnement multithreadé pour éviter un comportement inattendu. En utilisant des verrous, vous pouvez protéger efficacement les ressources partagées. Le modèle singleton, lorsqu’il est correctement implémenté avec des considérations de sécurité des threads, peut être puissant pour maintenir la stabilité et la correction de l’application.
En fin de compte, comprendre la sécurité des threads dans votre singleton devient une compétence essentielle pour tout développeur C# travaillant sur des applications complexes.