2008-11-28 6 views
3
abstract class Foo 
{ 
    private List<Object> container; 
    private bool update; 

    Foo Foo() 
    { 
     container = new List<object>(); 
     update = false; 
    } 

    public abstract Bar CreateBar(); 

    public void BeginUpdate() 
    { 
     if (!update) 
     { 
      Thread update_thread = new Thread(new ThreadStart(Update)); 
      update_thread.Start(); 
     } 
    } 

    private void Update() 
    { 
     update = true; 
     while (update) 
     { 
      lock (container) 
      { 
       if (...) 
        container.Add(this.CreateBar()); 
       else 
        container.Remove(...); 
      } 

      Thread.Sleep(1337); 
     } 
    } 

    public void EndUpdate() 
    { 
     update = false; 
    } 

    public List<Object> Objects 
    { 
     get 
     { 
      lock (container) 
      { 
       return this.container; 
      } 
     } 
    } 
} 

Quand quelque chose en dehors de Foo appelle l'accesseur de l'objet Foo comme,Comment le verrouillage se comportera-t-il dans .net?

List<Objects> objects = foo_instance.Objects; 
foreach (Object o in objects) 
{ 
    Thread.Sleep(31173); 
} 

Comment se produira le verrouillage? Est-ce que le thread qui exécute Update() doit attendre que le foreach ci-dessus ait fini de traiter la liste des objets? Je voudrais que ces deux travaillent simultanément, est la seule solution pour faire une copie profonde des objets?

Répondre

2

Votre code ne fait pas ce que vous pensez qu'il fait. Cette méthode

public List<Object> Objects 
{ 
    get 
    { 
     lock (container) 
     { 
      return this.container; 
     } 
    } 
} 

Ne conserve pas le verrou après avoir renvoyé la valeur. Donc, votre boucle n'est pas verrouillée.

Vous ne pouvez pas retourner l'instance contenant des clas

1

Vous pouvez penser à une portée de verrou similaire à la portée de la fonction. Faire un verrou à l'intérieur de votre méthode accesseur verrouillera uniquement entre les accolades, une fois la valeur retournée, le verrou sera libéré. Vous aurez besoin de faire le verrouillage en dehors de la classe (dans l'appelant) pour obtenir l'effet désiré.

Ainsi, la boucle et le thread pourront accéder aux objets en même temps. C'est mauvais, car si votre thread change d'objet pendant la boucle, la boucle lèvera une exception car la collection a été modifiée.

3

Plusieurs problèmes avec ce code:

  1. Vous ne commencez pas le fil
  2. Vous pouvez avoir une condition de course (pourrait ne pas être applicable à votre programme) où plusieurs threads appellent BeginUpdate, les deux voient que la mise à jour est false, et les deux démarrent un thread, maintenant vous avez deux threads en cours d'exécution, interférant entre eux, puisque vous avez un membre de champ avec les données partagées
  3. Quel est le point de verrouillage dans la propriété? Vous n'en avez pas besoin. Notez que le conteneur que vous renvoyez de la propriété sera parfois dans un état de flux en raison de la mise à jour interne. Cela signifie que le code externe qui appelle la propriété devra verrouiller le conteneur lui-même avant d'accéder à son contenu. Vous n'avez pas besoin d'un verrou pour renvoyer la référence du conteneur.
  4. condition de course si vous appelez EndUpdate puis BeginUpdate juste après l'autre, le vieux fil peut ne pas avoir eu la chance de sortir encore

Je dirais que, avant de commencer à se soucier de ce qu'il faut verrouiller et quand , vous devriez corriger le code pour qu'il n'ait pas beaucoup d'autres problèmes liés au thread.

+0

Merci, bon aperçu. Je devais vous donner une cerise verbale puisque je ne peux pas voter. ;> –

0

krosenvald est correct, le verrou sur l'accesseur d'objets est libéré dès que la propriété renvoie le pointeur vers l'objet conteneur ...

Dans votre code

List<Objects> objects = foo_instance.Objects; 
foreach (Object o in objects) 
{  
    Thread.Sleep(31173); 
} 

La serrure ne dure que la première ligne ... où la variable de référence "objets" est peuplée ... Il n'y a vraiment pas besoin de verrouiller quoi que ce soit pour cette ligne puisque aucune mémoire n'est modifiée en récupérant le pointeur ...

+0

Y aurait-il une violation d'accès possible si l'objet 'o' était utilisé et accédé à l'intérieur du foreach? –

+0

Eh bien, que voulez-vous dire par "violation d'accès"? Il n'y aura certainement pas de conflit de verrous, car la cible du verrou aura libéré la locj au moment où le foreach commencera ... –

Questions connexes