Quelques précisions sur le modèle SyncRoot: quelle est la bonne façon d’utiliser ce modèle?

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:

  1. Aucun object SyncRoot privé ne doit être exposé via une propriété publique; en d’autres termes, une structure de données personnalisée ne doit pas fournir de propriété SyncRoot publique (voir aussi ce commentaire ).
  2. En général, il n’est pas obligatoire d’utiliser des objects privés pour le locking (voir la présente réponse ).
  3. Si une classe a plusieurs ensembles d’opérations devant être synchronisés, mais pas les uns avec les autres, elle devrait avoir plusieurs objects SyncRoot privés (voir 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.