2011-12-18 4 views
5

semplificato la questione di dare una rappresentazione più chiara di quello che sto chiedendo in realtàarray di oggetti, l'aggiornamento in un thread e la lettura in un altro

Ho due fili, li chiamano A e B. Condivide un oggetto di tipo Foo che ha un campo chiamato Name e viene memorizzato in un array di tipo Foo[] all'indice 0. I thread accedono sempre all'indice 0 in un ordine che è già garantito dal sistema, quindi non esiste alcuna condizione di competizione della filettatura B preceduta dal thread A.

L'ordine è questo.

// Thread A 
array[0].Name = "Jason"; 

// Thread B 
string theName = array[0].Name 

Come ho detto questo ordine è già garantito, non c'è modo per filo B per leggere il valore prima di infilare un

Quello che voglio garantire è di due cose:

  1. Entrambi i thread ottengono l'ultimo oggetto all'indice 0.
  2. Il thread B ottiene sempre l'ultimo valore nel campo .Name

La marcatura di Name come volatile non è un'opzione, in quanto gli oggetti reali sono molto più complessi e dispongono anche di strutture personalizzate che non possono nemmeno avere l'attributo volatile ad essi collegato.

Ora, soddisfacendo 1 è facile (ottenendo sempre l'ultimo oggetto), si può fare un .VolatileRead:

// Thread A 
Foo obj = (Foo)Thread.VolatileRead(ref array[0]); 
obj.Name = "Jason"; 

// Thread B 
Foo obj = (Foo)Thread.VolatileRead(ref array[0]); 
string theName = obj.Name 

Oppure è possibile inserire una barriera di memoria:

// Thread A 
array[0].Name = "Jason"; 
Thread.MemoryBarrier(); 

// Thread B 
Thread.MemoryBarrier(); 
string theName = array[0].Name 

Quindi la mia domanda è: è sufficiente per soddisfare anche la condizione 2? Che ottengo sempre l'ultimo valore dai campi dell'oggetto che ho letto? Se l'oggetto nell'indice 0 non è cambiato, ma lo è Name. Effettuando un VolatileRead o un 0 sull'indice , assicurarsi che tutti i campi nell'oggetto all'indice 0 ottengano anche i loro ultimi valori?

+1

Perché stai usando gli array? Guarda lo spazio dei nomi ['System.Collections.Concurrent'] (http://msdn.microsoft.com/en-us/library/system.collections.concurrent.aspx). – Oded

+0

Non usando .NET4, per cominciare. E le cose Concurrent, per quanto ne so, non fanno che garantire il contenuto delle collezioni stesse e non i possibili campi/proprietà degli oggetti nelle collezioni. – thr

+0

Non penso che volatile garantirebbe quello che pensi che sarebbe. –

risposta

2

Nessuna di queste soluzioni, lock o volatile risolverà il problema. Perché:

  1. volatile assicura che le variabili modificate da un thread sono immediatamente visibili ad altri fili presenti sugli stessi dati (cioè esse non vengono memorizzate) e anche che le operazioni su tale variabile non vengono riordinati. Non proprio quello di cui hai bisogno.
  2. lock assicura che la scrittura/lettura non avvenga simultaneamente ma non garantisce il loro ordine. Dipende da quale thread ha acquisito il blocco prima, che non è deterministico.

Pertanto, se il flusso è:

Thread A read Name 
Thread A modify Name 
Thread B read Name 

esattamente in questo ordine, sarà necessario farla rispettare con un evento (ad esempio AutoresetEvent per esempio):

//Thread A 
foo[0].Name = "John"; // write value 
event.Set(); // signal B that write is completed 

//Thread B 
event.WaitOne(); // wait for signal 
string name = foo[0].Name; // read value 

Questo garanzie quel thread B non legge la variabile Name finché A non lo ha modificato.

Modifica: Ok, quindi sei sicuro che il flusso di cui sopra è rispettato. Dal momento che si sta dicendo che non è possibile dichiarare i campi volatile, vi consiglio l'uso di Thread.MemoryBarrier() di introdurre recinzioni che applicano ordinazione:

//Thread A 
foo[0].Name = "John"; // write value 
Thread.MemoryBarrier(); 

//Thread B 
Thread.MemoryBarrier(); 
string name = foo[0].Name; // read value 

Per maggiori informazioni, consultare questo documento: http://www.albahari.com/threading/part4.aspx

+0

Questo non è un problema, due thread non possono accedervi allo stesso tempo. Mai. Penso che le persone non capiscano la mia domanda e ho bisogno di riscriverla. – thr

+0

L'accesso sarà sempre sequenziale, ma da due (o forse più) thread differenti. – thr

+0

@thr: Da questa dichiarazione: "Ora per essere in grado di garantire che Thread1 riceva il nuovo valore memorizzato nel campo Nome di Foo ..." Capisco che è necessario Thread1 per leggere Nome solo dopo che Thread0 ha finito di aggiornarlo. – Tudor

0

Locks potranno affrontare il problema hai se lo capisco correttamente. Questo perché il blocco genera barriere della memoria implicite (complete) attorno a sé.

È inoltre possibile utilizzare la barriera di memoria in modo esplicito utilizzando Thread.MemoryBarrier. Maggiori informazioni here. L'effetto barriera di memoria può essere piuttosto difficile da notare su x86, ma su un sistema di ordinamento più rilassato come PPC è spesso notevole.

Problemi correlati