2011-11-26 7 views
8

ho preso il codice di DCL dal libro di Joe Duffy 'Programmazione concorrente sulle finestre'Che tipo di operazione 'volatili' è necessario in ricontrollato blocco in .NET

class LazyInit<T> where T : class 
{ 
private volatile T m_value; 
private object m_sync = new object(); 
private Func<T> m_factory; 
public LazyInit(Func<T> factory) { m_factory = factory; } 
public T value 
{ 
    get 
    { 
    if (m_value == null) 
    { 
     lock (m_sync) 
     { 
     if (m_value == null) 
     { 
      m_value = m_factory(); 
     } 
     } 
    } 
    return m_value; 
    } 
} 
} 

si dice marcatura m_value volatili può prevenire scrive riordinamenti che porteranno ad altri thread ottenendo 'oggetto non nullo con campi non inizializzati'. Se il problema si verifica solo perché le possibili scritture possono essere riordinate, posso semplicemente usare "Volatile Write" invece di contrassegnare il file volatile, come di seguito? (Questo codice è un po 'imbarazzante per la dimostrazione, voglio solo per assicurarsi che, se siamo in grado di utilizzare solo scrittura volatili invece)

class LazyInit<T> where T : class 
{ 
private object m_value; 
private object m_sync = new object(); 
private Func<T> m_factory; 
public LazyInit(Func<T> factory) { m_factory = factory; } 
public T value 
{ 
    get 
    { 
    if (m_value == null) 
    { 
     lock (m_sync) 
     { 
     if (m_value == null) 
     { 
      Thread.VolatileWrite(ref m_value, m_factory()); 
     } 
     } 
    } 
    return (T)m_value; 
    } 
} 
} 

Una questione correlata è la versione Interlocked dal libro

class LazylnitRelaxedRef<T> where T : class 
{ 
private volatile T m_value; 
private Func<T> m_factory; 
public LazylnitRelaxedRef(Func<T> factory) { m_factory = factory; } 
public T Value 
{ 
    get 
    { 
    if (m_value == null) 
     Interlocked.CompareExchange(ref m_value, m_factory(), null); 
    return m_value; 
    } 
} 
} 

Dal le specifiche ECMA-CLI "L'operazione interbloccata esegue operazioni di acquisizione/rilascio implicite", abbiamo ancora bisogno di volatilità in questo caso?

+2

C'è una differenza importante tra il modello di memoria ECMA e il modello di memoria .net 2. – CodesInChaos

+0

Perché è necessario eseguire il doppio controllo di blocco? Hai notato un problema di prestazioni con la tua applicazione con l'acquisizione di un lucchetto? –

+1

@CodeInChaos, si prega di approfondire su quale sia la "differenza importante". – Amy

risposta

1

In primo luogo, fare casino con volatile è davvero difficile, quindi non perdetevelo! Ma, here è una risposta molto vicino alla tua domanda, e here è un articolo che penso che tutti dovrebbero leggere prima di utilizzare la parola chiave volatile, e sicuramente prima di iniziare a usare VolatileRead, VolatileWrite e MemoryBarrier.

La risposta nel primo link è: no, non è necessario utilizzare volatili, basta utilizzare System.Threading.Thread.MemoryBarrier() destra prima di assegnare il nuovo valore. Questo perché il release_fence implicito quando si utilizza la parola chiave volatile si assicura che sia terminato di scrivere nella memoria principale e che nessuna operazione di lettura/scrittura possa essere eseguita fino al termine.

Quindi, cosa fa Thread.VolatileWrite() e ha le stesse funzioni che otteniamo dalla parola chiave "volatile"? Bene, ecco il codice completo da questa funzione:

public static void VolatileWrite (ref int address, int value) 
{ 
    MemoryBarrier(); address = value; 
} 

Sì, chiama MemoryBarrier destra prima assegna il vostro valore, il che è sufficiente!

+0

, Scusate se vi ho ingannato dal codice, dovevo chiedere, per il DCL, c'è qualche problema in alcuni modelli di memoria più deboli se la lettura non è volatile o non si applica acquisire recinzione in lettura? A proposito, .net utilizza il recinto completo per l'implementazione dell'API Thread.VolatileRead e Thread.VolatileWrite, il che significa che sono più forti di una lettura volatile e di una scrittura volatile. Per la mia domanda, consideriamo il caso volatile di lettura, non quello dell'API. (Ancora una volta, scusa per averti ingannato dal codice) – Fei

+1

Il comportamento volatile originale è quello di mettere una barriera prima della scrittura e dopo la lettura. Suggerirei una barriera di memoria prima e dopo le operazioni di scrittura e prima di un'operazione di lettura. Se scriviamo e leggiamo subito dopo, rischiamo di scambiare queste due operazioni a causa di nessuna barriera tra di esse. –

+0

@Ivaylo Slavov, per "scambiato", potresti darmi ulteriori informazioni su cosa accadrebbe in questo caso per rompere il DCL? A proposito, questo "scambio" interromperà la dipendenza dai dati? – Fei

Problemi correlati