2012-09-14 8 views
8

Ho letto qualcosa sullo SyncRoot pattern come regola generale per evitare deadlock. E leggendo una domanda di alcuni anni fa (vedi questo link), penso di capire che alcuni usi di questo modello potrebbero non essere corretti. In particolare, mi sono concentrato sui seguenti frasi da this topic:Alcuni chiarimenti sul pattern SyncRoot: qual è il modo corretto di usare questo pattern?

Noterete una proprietà SyncRoot su molte delle collezioni in System.Collections. In retrospeced, ritengo che questa proprietà fosse un errore ... Non ci vergognamo di commettere lo stesso errore mentre costruiamo le versioni generiche di queste raccolte.

Infatti, per esempio, List<T> classe non implementa SyncRoot proprietà, o più correttamente è implementato in modo esplicito (vedi this answer), quindi è necessario eseguire il cast a ICollection al fine di utilizzarlo. Ma il numero this comment afferma che rendere pubblico un campo privato SyncRoot è una cattiva pratica come il blocco su this (vedere this answer), come confermato anche in this comment.

Quindi, se ho capito bene, quando implemento una struttura dati non thread-safe, dal momento che potrebbe essere utilizzata in un contesto multithread, non dovrei (in realtà, non devo) fornire la proprietà SyncRoot. Ma dovrei lasciare lo sviluppatore (che userà questa struttura dati) con il compito di associarlo a un oggetto SyncRoot privato come nel seguente codice di esempio.

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 
     } 
    } 
} 

In sintesi, ho capito che le migliori pratiche per la sincronizzazione/bloccaggio dovrebbe essere la seguente:

  1. Tutti gli oggetti SyncRoot privati ​​non dovrebbero essere esposti tramite una proprietà pubblica; in altre parole, una struttura dati personalizzata non dovrebbe fornire una proprietà pubblica SyncRoot (vedere anche this comment).
  2. In generale, non è obbligatorio utilizzare oggetti privati ​​per il blocco (vedere this answer).
  3. Se una classe ha più serie di operazioni che devono essere sincronizzate, ma non tra loro, dovrebbe avere più oggetti SyncRoot privati ​​(vedere this comment).

È quanto scritto sopra l'uso corretto di questo modello?

+0

La mia opinione è che se si vuole rendere internamente una classe thread-safe, si esegue il lock internamente (privatamente) e si documenta che la classe è generalmente thread-safe. Altrimenti, fai _no locking_, documenta che la classe non è thread-safe e lascia che i consumatori della tua classe implementino il proprio lock attorno a loro come necessario per il loro uso specifico. –

risposta

4

vorrei evitare di aggiungere una proprietà SyncRoot al tipo che disegno, qui sono le ragioni:

  • Gli utenti di mio tipo può essere necessario utilizzare meccanismo di sincronizzazione diverso, ad esempio Mutex, o ReaderWriterLock o ReaderWriterLockSlim ecc

  • Il tipo diventa più grasso: la sua responsabilità diventa più dispersa. Perché dovrei aggiungere il supporto per il blocco multithreading esplicito e nessun supporto per altri fluff? Vorrei obbligare l'utente a seguire una sola pratica, che non può essere la soluzione migliore in tutti i casi

  • avrei bisogno di attuare correttamente la proprietà (senza ritorno this o typeof(MyClass)), vale a dire questo è sbagliato:

    public object SyncRoot {get {return this;}} 
    

Eviterei anche l'utilizzo della proprietà SyncRoot dai tipi di framework .NET. Se devo creare un tipo senza la proprietà SyncRoot, userò un modello di blocco, e se un tipo ha questa proprietà non continuerò a optare per il blocco su SyncRoot. Ciò rende il mio stile di codice coerente e più facile da leggere/mantenere.

0

Qui ci sono diversi concetti. In primo luogo, ciò che è stato implementato correttamente è una classe thread-safe in cui nessun utente della classe dovrebbe eseguire la propria sincronizzazione. Pertanto non è assolutamente necessario esporre l'oggetto syncRoot. Nelle vecchie classi Collection la proprietà SyncRoot è stata esposta perché le classi erano non thread-safe.

Quando si blocca un oggetto arbitrario e si blocca la raccolta interna, non c'è assolutamente alcuna differenza in termini di correttezza o prestazioni del programma. Finché il riferimento ad entrambi non cambia, funzionano altrettanto bene come i parametri di Monitor.Enter/Exit. La tua collezione interiore cambierà? Se no, contrassegnalo anche come readonly.

In terzo luogo, vi è il commento relativo all'utilizzo di diversi blocchi in base a diverse operazioni. Il classico esempio di questo è ReaderWriterLock.È necessario analizzare la necessità di utilizzare diversi livelli di blocchi in base alle diverse funzionalità esposte dalla classe.

Problemi correlati