2011-01-18 14 views
37

Una domanda come la mia è stata asked, ma la mia è un po 'diversa. La domanda è: "Perché la parola chiave volatile non è consentita in C# sui tipi System.Double e System.Int64, ecc.?"Perché non volatile su System.Double e System.Long?

In un primo momento, ho risposto al mio collega, "Beh, su una macchina a 32 bit, quei tipi richiedono almeno due zecche per entrare anche nel processore, e il framework .Net ha l'intenzione di astrarre via il processore specifico dettagli del genere. " A cui risponde, "Non sta astratizzando nulla se ti impedisce di usare una funzione a causa di un problema specifico del processore!"

Sta implicando che un dettaglio specifico del processore non deve apparire a una persona che utilizza un framework che "astrae" dettagli del genere dal programmatore. Quindi, il framework (o C#) dovrebbe allontanare quelli e fare ciò che deve fare per offrire le stesse garanzie per System.Double, ecc. (Sia che si tratti di un semaforo, di una barriera di memoria o di qualsiasi altra cosa). Ho sostenuto che il framework non dovrebbe aggiungere il sovraccarico di un semaforo su volatile, perché il programmatore non si aspetta un sovraccarico con tale parola chiave, perché un semaforo non è necessario per i tipi a 32 bit. Il sovraccarico maggiore per i tipi a 64 bit potrebbe essere una sorpresa, quindi, per il framework .Net, è meglio non permetterlo e farti fare il tuo semaforo su tipi più grandi se il sovraccarico è accettabile.

Ciò ha portato a indagare su cosa sia la parola chiave volatile. (vedi la pagina this). Nella pagina viene riportato, nelle note:

In C#, utilizzando il modificatore volatile su un campo garantisce che tutti gli accessi a tale campo utilizzino VolatileRead o VolatileWrite.

Hmmm ..... VolatileRead e VolatileWrite sia sostenere i nostri tipi a 64 bit !! La mia domanda, allora, è,

"Perché la parola chiave volatile non ammessi in C# sui tipi System.Double e System.Int64, ecc?"

+2

Si prega di notare che Microsoft ha corretto la pagina e ** non ** dice 'In C#, utilizzando il modificatore volatile, su un campo garantisce che tutti gli accessi a quel campo utilizza VolatileRead o VolatileWrite.' –

risposta

4
Non

davvero una risposta alla tua domanda, ma ...

Sono abbastanza sicuro che la documentazione di MSDN che hai fatto riferimento non è corretta quando afferma che "utilizzando il modificatore volatile, su un campo garantisce che tutti gli accessi a tale campo utilizzano VolatileRead o VolatileWrite ".

La lettura diretta o la scrittura su un campo volatile genera solo un mezzo recinto (una recinzione di acquisizione durante la lettura e una barriera di rilascio durante la scrittura).

I metodi VolatileRead e VolatileWrite utilizzano internamente MemoryBarrier, che genera una recinzione completa.

Joe Duffy conosce una o due cose sulla programmazione concorrente; this is what he has to say about volatile:

(Per inciso, molti si chiedono circa la differenza tra i carichi e negozi di variabili contrassegnati come volatili e invita a Thread.VolatileRead e Thread.VolatileWrite La differenza è che il primo. API vengono implementate più forte che il codice jitted : essi raggiungono acquisire/rilascio semantica attraverso l'emissione di recinzioni complete sul lato destro le API sono più costosi chiamare troppo, ma almeno consentono di decidere su un.base callsite-by-callsite che singoli carichi e negozi hanno bisogno dei garanzie MM.)

+0

ho sentito un'altra opinione: http://igoro.com/archive/volatile-keyword-in-c-memory-model-explained/ scorrere fino alla tabella nella sezione "Modello di memoria e operazioni .NET" – Andrey

+0

strano, "in realtà generare pieni recinti ", http://www.albahari.com/threading/part4.aspx – Andrey

+0

@Andrey: Sia' VolatileRead' che 'VolatileWrite' chiamano' MemoryBarrier' internamente. Quindi non vedo come possano avere semantiche più deboli di 'MemoryBarrier' da solo, che è quello che suggerisce la tabella. – LukeH

11

Sì. Il motivo è che non puoi nemmeno leggere double o long in un'unica operazione. Sono d'accordo che si tratta di scarsa astrazione. Ho la sensazione che la ragione fosse che leggerli atomicamente richiede uno sforzo e sarebbe troppo intelligente per il compilatore. Quindi ti lasciano scegliere la soluzione migliore: lock ing, Interlocked, ecc.

La cosa interessante è che possono essere letti in modo atomico su 32 bit utilizzando i registri MMX. Questo è ciò che fa il compilatore java JIT. E possono essere letti atomicamente su macchine a 64 bit. Quindi penso che sia un difetto grave nel design.

+3

Il punto sarebbe se può essere letto/scritto atomicamente su qualsiasi implementazione della CLI. –

+0

"Ho la sensazione che la ragione sia che leggerli atomicamente richiede uno sforzo e sarebbe troppo intelligente per il compilatore." Ma non è più intelligente rispetto agli altri tipi.Ad ogni modo, usa 'System.Threading.Thread.VolatileRead' per' System.Double' e 'System.Int32'. Non ci vuole più "intelligenza" o altro per fare semplicemente quello che stai facendo alle altre varibales. –

+3

@Henk Tutte le implementazioni della CLI hanno 'System.Threading.Thread.VolatileRead' giusto? O forse non capisco il tuo significato. –

13

Sta lasciando intendere che un dettaglio specifico del processore non dovrebbe mostrare fino a una persona che utilizza un quadro che "abstract" dettagli del genere lontani dal programmatore.

Se si utilizza tecniche a basso-lock come i campi volatili, barriere memoria esplicita, e simili, allora siete interamente nel mondo dei particolari specifiche del processore. È necessario comprendere a livello profondo esattamente ciò che il processore è e non è autorizzato a fare, per quanto riguarda il riordino, la coerenza e così via, al fine di scrivere programmi corretti, portatili e robusti che utilizzino tecniche di basso lock.

Il punto di questa caratteristica è dire "Sono abbandonando astrazioni convenienti garantita dalla programmazione thread singolo e abbraccia le prestazioni guadagna possibile avendo una conoscenza approfondita attuazione specifiche della mia processore". Dovresti aspettarti meno astrazioni a tua disposizione quando inizi a utilizzare tecniche di blocco basso, non altre astrazioni.

Stai andando "giù al metal" per una ragione, presumibilmente; il prezzo che si paga è dover affrontare le stranezze di detto metallo.

+0

potresti spiegare perché 'volatile' non è permesso per' long' e 'double'? teoricamente è possibile fare alcuni trucchi per renderli volatili. – Andrey

+6

@Andrey: Certo, il CLR ci permette di rendere l'accesso volatile, non atomico ai campi di tipo double (penso ... non l'ho provato). I progettisti C# hanno deciso di non permetterlo; tutti gli accessi volatili sono anche atomici in C#. Se sei il tipo di persona che ha bisogno di accesso volatile ma non atomico a variabili contenenti strutture di grandi dimensioni, allora C# potrebbe non essere la lingua migliore per te. Se si desidera utilizzare C# per questo, è sempre possibile utilizzare VolatileRead e VolatileWrite; si noti che così facendo * in pratica * provoca una barriera di memoria completa, anche se non è una * garanzia * delle API e potrebbe cambiare in futuro. –

+0

Grazie per il tuo commento. Hai menzionato "... con una profonda conoscenza specifica dell'implementazione del mio processore". subito dopo aver detto "scrivere ... portatile ... programmi ...". Quindi, siamo interessati al "mio" processore o portabilità (all'incirca a tutti i processori o alla programmazione difensiva)? Sono certamente d'accordo sul fatto che più si ottiene il "metallo" (più threading è l'argomento), più "implementazione".Penserei, tuttavia, che una parola chiave come volatile potrebbe offrire una soluzione agnostica per l'implementazione, qualcosa del tipo "questa parola chiave avvolge 'VolatileRead' e' VolatileWrite' ". Cosa ne pensi? –

3

È una semplice spiegazione dell'eredità. Se leggi questo articolo - http://msdn.microsoft.com/en-au/magazine/cc163715.aspx, troverai che l'unica implementazione del runtime .NET Framework 1.x era su macchine x86, quindi è logico che Microsoft la implementa sul modello di memoria x86. x64 e IA64 sono stati aggiunti in seguito. Quindi il modello di memoria di base era sempre uno di x86.

Potrebbe essere stato implementato per x86? In realtà non sono sicuro che possa essere pienamente implementato - un ref di un double restituito dal codice nativo potrebbe essere allineato a 4 byte anziché 8. In tal caso, tutte le tue garanzie di lettura/scrittura atomica non valgono più.

+1

Sembra una risposta eccellente. –

Problemi correlati