J’ai lu quelque chose sur le modèle SyncRoot en règle générale pour éviter les blocages. Et en lisant une question posée il y a quelques années (voir ce lien ), je pense comprendre que certaines utilisations de ce modèle peuvent être incorrectes. En particulier, je me suis concentré sur les phrases suivantes de ce sujet :
Vous remarquerez une propriété SyncRoot sur de nombreuses collections dans System.Collections. Dans Retrospeced, je pense que cette propriété était une erreur … Rassurez-vous, nous ne ferons pas la même erreur en construisant les versions génériques de ces collections.
En fait, par exemple, la classe List
SyncRoot
propriété SyncRoot
, ou plus correctement, elle est implémentée explicitement (voir la réponse ), vous devez donc effectuer un ICollection
vers ICollection
pour pouvoir l’utiliser. Mais ce commentaire indique que rendre un champ privé SyncRoot
public est aussi une mauvaise pratique que de le verrouiller (voir cette réponse ), comme le confirme également ce commentaire .
Donc, si je comprends bien, lorsque j’implémente une structure de données non thread-safe, puisqu’elle peut être utilisée dans un contexte multithread, je ne devrais pas (en fait, je ne dois pas) fournir la propriété SyncRoot
. Mais je devrais laisser au développeur (qui utilisera cette structure de données) la tâche de l’associer à un object privé SyncRoot, comme dans l’exemple de code suivant.
public class A { private MyNonThreadSafeDataStructure list; private readonly object list_SyncRoot = new object; public Method1() { lock(list_SyncRoot) { // access to "list" private field } } public Method2() { lock(list_SyncRoot) { // access to "list" private field } } }
En résumé, j’ai compris que les meilleures pratiques de synchronisation / locking devraient être les suivantes:
SyncRoot
publique (voir aussi ce commentaire ). Est-ce que ce qui est écrit ci-dessus est la bonne utilisation de ce modèle?
SyncRoot
append une propriété SyncRoot
au type que je conçois, voici les raisons:
Les utilisateurs de mon type peuvent avoir besoin d’utiliser un mécanisme de synchronisation différent, par exemple, Mutex
, ReaderWriterLock
ou ReaderWriterLockSlim
etc.
Le type devient plus gros: sa responsabilité devient plus dispersée. Pourquoi devrais-je append un support pour le locking multithreading explicite et aucun support pour d’autres fluff? J’oblige l’utilisateur à ne suivre qu’une pratique, ce qui peut ne pas être la meilleure solution dans tous les cas
J’aurais besoin d’implémenter la propriété correctement (pas de retour this
ou typeof(MyClass)
), c’est à dire que c’est faux:
public object SyncRoot {get {return this;}}
SyncRoot
également d’utiliser la propriété SyncRoot
partir des types de framework .NET. Si j’ai besoin de faire un type sans propriété SyncRoot
threadsafe, j’utiliserai un schéma de locking, et si un type a cette propriété, je ne choisirai toujours pas le locking sur SyncRoot
. Cela rend mon style de code cohérent et plus facile à lire / maintenir.
Il y a un certain nombre de concepts ici. Premièrement, ce que vous avez correctement implémenté est une classe thread-safe où aucun consommateur de la classe n’aurait à effectuer sa propre synchronisation. Par conséquent, vous n’avez absolument pas besoin d’exposer l’object syncRoot. Dans les anciennes classes de collection, la propriété SyncRoot était exposée car les classes n’étaient pas thread-safe.
Lorsque vous verrouillez un object arbitraire et votre collection interne, il n’ya absolument aucune différence en termes d’exactitude ou de performance du programme. Tant que la référence aux deux ne change pas, ils fonctionnent aussi bien que les parameters de Monitor.Enter / Exit. Votre collection intérieure va-t-elle changer? Si non, marquez-le aussi en lecture seule.
Troisièmement, il y a le commentaire concernant l’utilisation de différents verrous basés sur différentes opérations. L’exemple classique en est le ReaderWriterLock. Vous devriez parsingr la nécessité d’utiliser différents niveaux de verrous en fonction des différentes fonctionnalités exposées par votre classe.