Domanda Memory Barrier per dichiarazione di blocco


Ho letto di recente le barriere della memoria e il problema del riordino e ora ho una certa confusione a riguardo.

Si consideri il seguente scenario:

private object _object1 = null;    
private object _object2 = null;
private bool _usingObject1 = false;

private object MyObject
{
    get 
    {
        if (_usingObject1)
        {
            return _object1;
        }
        else
        {
            return _object2;
        }
    }
    set 
    {
        if (_usingObject1)
        {
           _object1 = value;
        }
        else
        {
           _object2 = value;
        }
    }
}

private void Update()
{
    _usingMethod1 = true;
    SomeProperty = FooMethod();
    //..
    _usingMethod1 = false;
}
  1. A Update metodo; è il _usingMethod1 = true affermazione eseguita sempre prima di ottenere o impostare la proprietà? o a causa di un problema di riordino, non possiamo garantirlo?

  2. Dovremmo usare volatile piace

    private volatile bool _usingMethod1 = false;
    
  3. Se usiamo lock; possiamo garantire che ogni dichiarazione all'interno del blocco verrà eseguita in ordine come:

    private void FooMethod()
    {
        object locker = new object();
        lock (locker)
        {
            x = 1;
            y = a;
            i++;
        }
    }
    

12
2018-05-16 16:16


origine


risposte:


Il tema delle barriere della memoria è piuttosto complesso. Di tanto in tanto, incassa persino gli esperti. Quando parliamo di una barriera di memoria, stiamo davvero combinando due idee diverse.

  • Acquisisci recinzione: una barriera di memoria in cui altre letture e scritture non possono muoversi prima la recinzione.
  • Recinzione: una barriera di memoria in cui altre letture e scritture non possono muoversi dopo la recinzione.

Una barriera di memoria che crea solo una delle due è talvolta chiamata a mezza recinzione. Una barriera di memoria che crea entrambi è talvolta chiamata a full-fence.

Il volatile la parola chiave crea mezzi recinti. Le letture di campi volatili hanno acquisito semantica mentre le scritture hanno una semantica di rilascio. Ciò significa che nessuna istruzione può essere spostata prima di una lettura o dopo una scrittura.

Il lock la parola chiave crea una recinzione completa su entrambi i limiti (entrata e uscita). Ciò significa che nessuna istruzione può essere spostata prima o dopo ciascun limite.

Tuttavia, tutto questo è discutibile se ci interessa solo un thread. L'ordine, così come viene percepito da quel thread, è sempre preservato. In realtà, senza quella garanzia fondamentale nessun programma avrebbe mai funzionato correttamente. Il vero problema è con come altro i thread percepiscono letture e scritture. È qui che devi essere preoccupato.

Quindi per rispondere alle tue domande:

  1. Dalla prospettiva di un singolo thread ... sì. Dalla prospettiva di un altro thread ... no.

  2. Dipende. Potrebbe funzionare, ma ho bisogno di avere una migliore comprensione di ciò che stai cercando di ottenere.

  3. Dalla prospettiva di un altro thread ... no. Le letture e le scritture sono libere di muoversi all'interno dei limiti della serratura. Non riescono a muoversi fuori da questi limiti. Questo è il motivo per cui è importante che anche altri thread creino barriere di memoria.


28
2018-05-16 20:42



La parola chiave volatile non raggiunge nulla qui. Ha garanzie molto deboli, non implica una barriera di memoria. Il tuo codice non mostra la creazione di un altro thread, quindi è difficile indovinare se è necessario il blocco. È tuttavia un requisito irrinunciabile se due thread possono eseguire Update () allo stesso tempo e utilizzare lo stesso oggetto.

Fai attenzione che il tuo codice di protezione come pubblicato non blocca nulla. Ogni thread avrebbe la propria istanza dell'oggetto "locker". Devi renderlo un campo privato della tua classe, creato dal costruttore o da un inizializzatore. Così:

private object locker = new object();

private void Update()
{
    lock (locker)
    {
        _usingMethod1 = true;
        SomeProperty = FooMethod();
        //..
        _usingMethod1 = false;
    }
}

Si noti che ci sarà anche una gara nell'assegnazione SomeProperty.


4
2018-05-16 17:44