2015-09-28 3 views
6

Navigando attraverso il codice sorgente MEF ho trovato questo pezzo. Qualcuno può spiegare perché è necessario MemoryBarrier all'interno di un lucchetto?Perché Thread.MemoryBarrier è utilizzato qui?

L'intero metodo è:

public void SatisfyImportsOnce(ComposablePart part) 
{ 
    this.ThrowIfDisposed(); 

    if (this._importEngine == null) 
    { 
     ImportEngine importEngine = new ImportEngine(this, this._compositionOptions); 

     lock(this._lock) 
     { 
      if (this._importEngine == null) 
      { 
       Thread.MemoryBarrier(); 
       this._importEngine = importEngine; 
       importEngine = null; 
      } 
     } 
     if(importEngine != null) 
     { 
      importEngine.Dispose(); 
     } 
    } 
    this._importEngine.SatisfyImportsOnce(part); 
} 
+0

E * Sembra * che a volte, il blocco non è sufficiente –

+0

E 'impossibile rispondere a questa domanda senza sapere molto di più contesto. –

+3

È FUD su un processore con un modello di memoria debole, alcuni programmatori Microsoft probabilmente non si riprenderanno mai dal dover domare l'Itanium. Assicura che un altro thread possa osservare l'oggetto completamente costruito quando utilizza il riferimento _importEngine. Su un processore debole tale riferimento potrebbe essere scritto in memoria prima che i campi oggetto vengano scritti in modo che un altro thread possa vedere il valore del campo non inizializzato. Non necessario dal momento che .NET 2.0 e sicuramente non necessario qui poiché il blocco implica già una barriera di memoria. –

risposta

1

Thread.MemoryBarrier impedisce jitter/compilatore eventuali istruzioni di riordino per l'ottimizzazione del codice.

Nel Treading in C#, by Joe Albahari libro egli sais:

  • Il compilatore, CLR, o CPU possono riordinare le istruzioni del programma per migliorare l'efficienza.
  • Il compilatore, CLR o CPU possono introdurre ottimizzazioni di memorizzazione nella cache in modo tale che le assegnazioni alle variabili non siano immediatamente visibili ad altri thread.

In questo esempio potrebbe essere che importEngine o valori _importEngine vengono memorizzati nella cache ed è molto importante che tutte le discussioni deve essere avvisato sui cambiamenti subito.

Anche MemoryBarrier in questo caso fornisce importEngine freschezza GARANZIA prima assegnato a _importEngine

+1

Ma la serratura implica anche un ricordo barocco. Né "importEngine" né "_importEngine" potrebbero essere memorizzati nella cache all'interno del blocco. (a meno che altre cose scrivano su "_importEngine" senza bloccare su "this._lock"). –

+0

@ScottChamberlain sei corretto. Ed è per questo che penso che MemoryBarrier sia usato in questo caso, ci potrebbero essere gli altri thread che scrive su _importEnginge senza usare _lock. –

+2

Nella CLI (la versione standardizzata del CLR) i controlli null di base sull'idioma di blocco a doppio controllo non sono sufficienti. Il modello di memoria della CLI richiederebbe che la variabile sia volatile o l'uso di una barriera esplicita. Il CLR ha un modello di memoria più forte, e non è necessaria né una barriera né una barriera. Gli autori di quel codice potrebbero non essere a conoscenza della garanzia extra del CLR, o come Jon Skeet preferisce non fare affidamento su di essi. La barriera ha un costo limitato, poiché una volta inizializzate e sincronizzate, le chiamate successive salteranno interamente il contenuto della serratura. –