2010-03-28 10 views
8

Ho fatto un semplice multi-threading in VB.NET per un po 'e sono appena entrato nel mio primo grande progetto multi-thread. Ho sempre fatto tutto usando la dichiarazione Synclock perché non pensavo ci fosse un modo migliore.Perché utilizzare SyncLocks in .NET per operazioni semplici quando è disponibile la classe Interlocked?

ho appena imparato a conoscere il Interlocked Class - lo fa sembrare come se tutto questo:

Private SomeInt as Integer 
Private SomeInt_LockObject as New Object 

Public Sub IntrementSomeInt 
    Synclock SomeInt_LockObject 
     SomeInt += 1 
    End Synclock 
End Sub 

può essere sostituito con una singola istruzione:

Interlocked.Increment(SomeInt) 

Questo gestisce tutto il bloccaggio all'interno e modifica il numero. Sarebbe molto più semplice che scrivere i miei blocchi per operazioni semplici (operazioni più lunghe o più complicate ovviamente hanno ancora bisogno del loro blocco).

C'è un motivo per cui eseguirò il mio blocco personale, utilizzando oggetti di blocco dedicati, quando posso eseguire la stessa operazione utilizzando i metodi Interlocked?

risposta

8

Sei corretto; Interlocked deve essere utilizzato qui e sarà più veloce di SyncLock.
Tuttavia, la classe Interlocked non è ben nota.

Tuttavia, ci sono situazioni in cui è necessario utilizzare SyncLock e Interlocked non aiuterà.

+0

Potete fornire un esempio di tale situazione? Se sto semplicemente facendo un semplice incremento/decremento (o .Aggiungere se ho bisogno di cambiare il valore di più di 1), sembra che Interlocked sarebbe meglio in tutti i casi. Se ho bisogno di qualcosa di più avanzato (come un processo in 4 fasi in cui ho bisogno di garantire che sia l'unico thread in esecuzione in un dato momento), ho bisogno di alcuni Synclocking. Sono interessato a una situazione in cui Interlocked sembra essere corretto, ma è una cattiva scelta. – SqlRyan

+3

Se stai lavorando con due variabili diverse, 'Interlocked' non è abbastanza. – SLaks

4

Questo perché nessuno lo sa. Diffondere la parola!

+1

+1 Buona risposta. – SLaks

+0

Questo è quello che pensavo - vedo che è in circolazione dall'1, e quindi sono rimasto sbalordito quando l'ho appena trovato. Ho pensato "Ci dev'essere qualcos'altro che non vedo, altrimenti questo metodo di blocco sarebbe molto più popolare".Voglio solo assicurarmi che sia così eccezionale sembra essere a prima vista. – SqlRyan

+1

Questa risposta non fornisce informazioni sufficienti, né risponde direttamente alla domanda. – Kir

3

La risposta è perché l'utilizzo di una serratura Monitor (SyncLock in VB e lock { } in C#) non solo assicura che solo un thread alla volta può accedere alla variabile (o, in senso stretto, un solo filo per volta può ottenere un blocco sull'oggetto di blocco), ma crea anche la barriera di memoria richiesta per garantire che le letture sulla variabile non vengano ottimizzate.

Se non stai semplicemente leggendo il valore della variabile (in altre parole, tutto il tuo lavoro viene svolto tramite chiamate a Interlocked), allora sarai a posto. Tuttavia, se è necessario essere in grado di eseguire una lettura normale della variabile, la situazione è più complicata. Le letture/scritture senza serratura sono generalmente eseguite in C# usando la parola chiave volatile. Questo ordina al compilatore di leggere il valore della variabile ovunque sia usato, piuttosto che l'ottimizzazione di una di queste letture in una cache locale. Sfortunatamente non esiste un equivalente in VB.NET, quindi dovrai usare qualcos'altro.

La risposta accettata allo this question dovrebbe fornire ulteriori informazioni su ciò che è possibile fare. In breve, molte persone usano SyncLock in VB.NET perché è più facile e meno complicato della logica necessaria per farlo senzaSyncLock.

+0

Non completamente corretto, quando si modificano le variabili con Interlock, è possibile leggerle senza alcuna misura speciale. Poiché la barriera è intorno alle scritture, le letture sono sicure. –

+0

@Henk: Non è possibile che più letture successive alla chiamata "Interlocked" siano ottimizzate in una singola lettura cache? –

+0

Adam, sì, potevano. E da una prospettiva in tempo reale che potrebbe sembrare "sbagliato", ma non comporterebbe un comportamento scorretto. Controlla i membri interbloccati. Esiste solo una lettura per valori a 64 bit (su sistemi a 32 bit). http://msdn.microsoft.com/en-us/library/system.threading.interlocked_members.aspx –

1

Interlocked è limitato alle operazioni semplici su Intero, Lungo e Booleano e così via.

Se si desidera aggiungere un elemento a un elenco condiviso (di T), ad esempio, sarà comunque necessario SynClock.

+0

Infatti, poiché è un'operazione atomica implementata a livello hardware, la CPU garantirà che l'incremento non può essere sostenuto, quindi deve essere mappato a primitive della CPU come inte, long ecc. – zebrabox

2

Una volta ho letto una spiegazione molto buona sulle cosiddette operazioni non atomiche e atomiche (in VB: interbloccate) e cercherò di riassumerla.

Normal "non-atomic" operations consist of several steps 

-> altri thread possono lavorare tra quelle streps

"Atomic" operations consist of one only one step 

-> altri thread non possono eseguire lavoro mentre operazioni atomiche vengono elaborati, operazioni atomiche sono sempre trattati come complesso

Il interbloccata class è una raccolta di tali operazioni atomiche e quindi per definizione thread-safe. Anche con più thread che eseguono operazioni di lettura e scrittura sulla stessa variabile, queste operazioni sono assolutamente thread-safe.

Ancora una combinazione di questi comandi thread-safe può essere non sicura, poiché le condizioni di gara possono verificarsi tra le operazioni atomiche.

Quindi, se si desidera confrontare 2 variabili e quindi incrementare quello più piccolo, questo non è thread-safe anche se le singole operazioni sono (interlocked.compare, interlocked.increment). Qui devi ancora usare i synclock.

Oltre a questa limitazione non esiste un "lato negativo nascosto" di interbloccato.

Un esempio di una race condition con a = 5:

Thread1: a+=1 
Thread2: a+=2  
--> supposed to be 8, but can be only 6 or 7, 
but can also be 6 or 7 depending on which thread wins the race 

opzione 1:

T1 step 1: read 5 
T1 step 2: add 1 = 6 
T1 step 3: write 6 
T2 step 1: read 6 
T2 step 2: add 2 = 8 
T2 step 3: write 8 
--> is 8 as supposed 

o Opzione 2:

T1 step 1: read 5 
T2 step 1: read 5 
T1 step 2: add 1 = 6 
T2 step 2: add 2 = 7 
T2 step 3: write 7 
T1 step 3: write 6 
--> is only 6 

o l'opzione 3:

Con interlocked.increment:

opzione 1:

T1 step 1: read 5, add 1, write 6 
T2 step 1: read 6, add 2, write 8 

o l'opzione 2:

T2 step 1: read 5, add 2, write 7 
T1 step 1: read 7, add 1, write 8 

-> in tutti i casi a = 8 come supposto, threadsafe soluzione

Tutto le domande che sono state pubblicate qui possono essere risolte applicando questo semplice esempio al codice discutibile.

Spero che questo aiuti altre persone che google questo argomento. Janis

Problemi correlati