2010-06-27 10 views
15

Durante la lettura della documentazione MSDN consente sempre di sapere se una classe è sicura o meno. La mia domanda è: come si progetta una classe per essere sicuri? Non sto parlando di chiamare la classe con il blocco I am significato che sto lavorando per Microsoft creare XXX class \ object e voglio dire che è "Thread Safe" cosa avrei bisogno di fare?Progettazione di una classe sicura discussione

+1

Vedere ad es. http://stackoverflow.com/questions/564319/achieving-thread-safety. –

+0

Non sto provando a TEST per la sicurezza dei thread Sto chiedendo che cosa è necessario prendere in considerazione per progettare per questo ... – Shane

risposta

8

Il modo più facile e più infallibile per rendere sicuro un thread di classe è quello di renderlo immutable. Il bello è che non devi mai preoccuparti di bloccare di nuovo.

Ricetta: crea tutte le variabili di istanza readonly in C# (final in Java).

  • Un oggetto immutabile, una volta creato e inizializzato nel costruttore, non può essere modificato.
  • Un oggetto immutabile è thread-safe. Periodo.
  • Questo non è lo stesso di avere una classe con solo costanti.
  • Per le parti mutabili del sistema, è ancora necessario tenere conto e gestire la proprietà di blocco/sincronizzazione. Questa è una ragione per scrivere classi immutabili in primo luogo.

Vedere anche questo question.

+1

Non sono d'accordo con te per 2 motivi. 1. Esistono diversi tipi di immutabilità con cui lavoriamo. http://blogs.msdn.com/b/ericlippert/archive/2007/11/13/immutability-in-c-part-one-kinds-of-immutability.aspx 2. "finale" è Java, tu significa renderli costanti? Se sì, non sono d'accordo. Rendere tutti i campi nelle costanti di classe è molto diverso dal renderli thread-safe – ram

+1

Essendo immutabile è inutile quando hai bisogno di thread per condividere effettivamente i dati (che sto asserendo è il caso basato sulla domanda). – Gabe

+1

Con finale intendo _make sicuro che ha un valore dopo il costruttore, e quel valore viene letto solo una volta assegnato_. Ci sono molti esempi là fuori, a dimostrazione del mio punto, ovvero http://blogs.msdn.com/b/lucabol/archive/2007/12/06/creating-an-immutable-value-object-in-c-part -ii-making-the-class-better.aspx http://pebblesteps.com/post/Introduction-to-Immutability-in-C.aspx –

4

Per affermare che la classe è thread-safe, si sta asserendo che le strutture di dati interne nella classe non saranno danneggiate dall'accesso simultaneo da più thread. Per rendere tale asserzione, è necessario introdurre il blocco (sincronizzazione in Java) attorno a sezioni critiche di codice all'interno della classe che potrebbero potenzialmente portare al danneggiamento di esse sono state eseguite da più thread simultanei.

+0

Non è necessario introdurre necessariamente il blocco. Puoi fare cose fantasiose con System.Threading.Interlocked *. – Spence

+0

Un altro, forse più semplice, modo per rendere tale asserzione quella di contrassegnare tutte le variabili di istanza 'readonly' per rendere la classe immutabile. –

0

non generico Le classi ICollection non forniscono proprietà per la sicurezza del thread. IsSynchronized e SyncRoot. sfortunatamente non puoi impostare IsSynchronized. Puoi leggere di più su di loro here

Nelle tue classi puoi avere qualcosa di simile a IsSynchronized e Syncroot, esporre solo metodi/proprietà pubbliche e all'interno del metodo body check per loro. Il tuo IsSynchronized sarà una proprietà di sola lettura, quindi una volta che l'istanza viene inizializzata, non sarà in grado di modificare lo

bool synchronized = true; 
var collection = new MyCustomCollection(synchronized); 
var results = collection.DoSomething(); 

public class MyCustomCollection 
{ 
    public readonly bool IsSynchronized; 
    public MyCustomCollection(bool synchronized) 
    { 
    IsSynchronized = synchronized 
    } 

    public ICollection DoSomething() 
    { 
    //am wondering if there is a better way to do it without using if/else 
    if(IsSynchronized) 
    { 
    lock(SyncRoot) 
    { 
     MyPrivateMethodToDoSomething(); 
    } 
    } 
    else 
    { 
     MyPrivateMethodToDoSomething(); 
    } 

    } 
} 

Si può leggere di più sulla scrittura di filo collezioni sicuri su Jared Parson's blog

0

La documentazione non lo fa suggerisco che le classi sono thread-safe, solo i metodi lo sono. Per affermare che un metodo è thread-safe, deve essere richiamabile da più thread contemporaneamente senza dare risultati errati (dove i risultati errati sarebbero il metodo che restituisce il valore sbagliato o l'oggetto che entra in uno stato non valido).

Quando la documentazione dice

statici pubblici (Shared in Visual Basic) di questo tipo sono thread sicuri.

probabilmente significa che i membri statici della classe non mutano lo stato condiviso.

Quando la documentazione dice

I membri di istanza non sono garantiti come thread-safe.

probabilmente significa che i metodi hanno un blocco interno minimo.

Quando la documentazione dice

Tutti i membri pubblici e protetti della questa classe sono thread-safe e possono essere utilizzato contemporaneamente da più thread.

probabilmente significa che tutti i metodi che è possibile chiamare utilizzano il blocco appropriato all'interno di essi. È anche possibile che i metodi non mutino nessuno stato condiviso, o che sia una struttura di dati senza blocco che in base alla progettazione consenta l'uso simultaneo senza alcun blocco.

0

Le classi di sicurezza del thread si occupano di proteggere i dati (variabili di istanza) della classe. Il modo più comune per farlo è utilizzare la parola chiave lock. L'errore newbie più comune è quello di utilizzare il blocco l'intera classe, invece di un blocco più a grana fine:

lock (this) 
{ 
    //do somethnig 
} 

Il problema di questo è che si può dare un importante calo di prestazioni se la classe fa qualcosa di importante. La regola generale è di bloccare il meno possibile il più breve possibile.

Si può leggere di più qui: lock keyword in C#

Dopo aver strarted a capire multithreadnig più a fondo si può anche dare un'occhiata a ReaderWriterLoch e il semaforo. Ma ti suggerisco di iniziare solo con la parola chiave lock.

5

In aggiunta alle altre risposte eccellenti qui, considera anche un altro angolo.

Non è sufficiente che la struttura interna dei dati della classe sia sicura al 100% se l'API pubblica ha operazioni a più fasi che non possono essere utilizzate in modalità thread-safe.

Si consideri una classe di elenchi che è stata creata in modo tale che, indipendentemente dal numero di thread effettuati indipendentemente dal tipo di operazioni su di esso, la struttura dei dati interni dell'elenco sarà sempre coerente e OK.

Considerate questo codice:

if (list.Count > 0) 
{ 
    var item = list[0]; 
} 

Il problema qui è che tra la lettura della proprietà Count e la lettura del primo elemento attraverso il [0] indicizzatore, un altro thread potrebbe aver sgomberato il contenuto della lista .

Questo tipo di sicurezza del thread viene in genere dimenticato quando viene creata l'API pubblica. Qui, l'unica soluzione è che il codice chiamante blocca manualmente qualcosa su ciascun tipo di accesso per impedire che il codice si arresti.

Un modo per risolvere questo sarebbe per il tipo di lista dell'autore di prendere in considerazione gli scenari tipici di utilizzo e aggiungere i metodi appropriati per il tipo:

public bool TryGetFirstElement(out T element) 

allora si avrebbe:

T element; 
if (list.TryGetFirstElement(out element)) 
{ 
    .... 

presumibilmente , TryGetFirstElement funziona in modo thread-safe e non restituirà mai true allo stesso tempo in quanto non è in grado di leggere il valore del primo elemento.

+1

Vale la pena notare che in molti casi può essere possibile per una classe specificare vari tipi di sicurezza del thread.Ad esempio, sarebbe molto più economico per un'implementazione di 'IDictionary ' per specificare che qualsiasi istanza su cui 'Delete' non è mai stato chiamato sarà thread-safe con un numero arbitrario di lettori e scrittori a condizione che nessun due scrittori agisca sul stessa chiave simultaneamente, e nessun lettore chiede una chiave il cui valore associato è stato modificato (ma che esisteva prima di tale modifica) a meno che 'TValue' sia un tipo di riferimento, piuttosto che promettere la sicurezza completa del thread. – supercat

Problemi correlati