2010-05-16 6 views
12

J'ai lu récemment sur les barrières de la mémoire et le problème de réorganisation et maintenant j'ai une certaine confusion à ce sujet.Mémoire Barrière par instruction lock

Envisagez le scénario suivant:

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. à Update méthode; l'instruction _usingMethod1 = true est-elle toujours exécutée avant d'obtenir ou de définir la propriété? ou en raison de problème de réorganisation, nous ne pouvons pas garantir cela?

  2. Doit-on utiliser volatile comme

    private volatile bool _usingMethod1 = false; 
    
  3. Si nous utilisons lock; pouvons-nous garantir alors chaque déclaration dans le verrou sera exécuté afin que:

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

Répondre

28

Le sujet des barrières de mémoire est assez complexe. Il arrête même les experts de temps en temps. Lorsque nous parlons d'une barrière de mémoire, nous combinons deux idées différentes.

  • Acquire clôture: Une barrière de mémoire dans laquelle d'autres lectures & ne sont pas autorisés écritures de se déplacer avant la clôture.
  • Clôture de publication: Une barrière de mémoire dans laquelle d'autres lectures & écrit ne sont pas autorisés à se déplacer après la clôture.

Une barrière de mémoire qui crée un seul des deux est parfois appelé un demi-barrière. Une barrière de mémoire qui crée les deux est parfois appelée full-fence.

Le mot clé volatile crée des demi-délimitations. Les lectures de champs volatiles acquièrent une sémantique tandis que les écritures ont une sémantique de libération. Cela signifie qu'aucune instruction ne peut être déplacée avant une lecture ou après une écriture.

Le mot clé lock crée des délimitations sur les deux limites (entrée et sortie). Cela signifie qu'aucune instruction ne peut être déplacée avant ou après chaque limite.

Cependant, tout cela est discutable si nous ne sommes concernés que par un seul thread. L'ordre, tel qu'il est perçu par ce fil, est toujours conservé.En fait, sans ce garant fondamental, aucun programme ne fonctionnerait jamais correctement. Le vrai problème est de savoir comment autres threads percevoir les lectures et écritures. C'est là que vous devez être concerné.

Donc, pour répondre à vos questions:

  1. Du point de vue d'un seul fil ... oui. Du point de vue d'un autre fil ... non.

  2. Cela dépend. Cela pourrait fonctionner, mais j'ai besoin de mieux comprendre ce que vous essayez d'accomplir.

  3. Du point de vue d'un autre thread ... non. Les lectures et les écritures sont libres de se déplacer dans les limites de la serrure. Ils ne peuvent tout simplement pas sortir de ces limites. C'est pourquoi il est important que les autres threads créent également des barrières de mémoire.

+0

Merci pour l'information, Cela m'aide vraiment à mieux comprendre le concept .. Ce que j'ai besoin de faire est de m'assurer que l'instruction "_usingMethod1 = true" sera toujours faite avant l'instruction suivante SomeProperty = FooMethod(); En multithread senario comment accomplir cela? est par: _usingMethod1 = true; Thread.MemoryBarrier(); SomeProperty = FooMethod(); ou verrouiller pour des clôtures complètes donc pas de réordonnancement: lock (locker) {_usingMethod1 = true; } SomeProperty = FooMethod(); ou peut-être simplement en faisant du _usingMethod1 une variable volatile. Merci de votre aide. –

+2

J'embaquerais tout le contenu de la méthode Update dans un verrou. En plus des barrières à la mémoire, il garantit également l'atomicité, ce qui est tout aussi important. En outre, ces idiomes sans verrou (via volatile, Thread.MemoryBarrier, etc.) sont incroyablement difficiles à obtenir. –

4

Le volatile mot-clé ne fait rien ici. Il a des garanties très faibles, cela n'implique pas de barrière de mémoire. Votre code ne montre pas qu'un autre thread est créé, il est donc difficile de deviner si le verrouillage est nécessaire. C'est cependant une exigence si deux threads peuvent exécuter Update() en même temps et utiliser le même objet.

Prenez garde que votre code de verrouillage tel que posté ne verrouille rien. Chaque thread aurait sa propre instance de l'objet "locker". Vous devez en faire un champ privé de votre classe, créé par le constructeur ou un initialiseur. Ainsi:

private object locker = new object(); 

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

Notez qu'il y aura également une course sur l'affectation SomeProperty.

+0

Le volatile a une barrière de mémoire; et ainsi j'ai demandé si cette barrière de momory avec elle supprimera le reordaring ainsi le _usingMethod1 = true sera toujours garanti pour excuter avant d'obtenir ou placer la propriété SomeProperty, Je fournissez un verrou juste pour la barrière de mémorisation pas pour le problème de syncronisation avec d'autres discussions et ainsi J'en fais une variable locale dans la méthode dans le but parce que je demandais si cela éviterait le réordonnancement des instructions à l'intérieur de la serrure. –

+1

Un seul thread * toujours * a une vue cohérente des variables qu'il utilise. Les programmes ne pourraient pas fonctionner si ce n'était pas le cas. –