2009-08-17 2 views
16

J'essaie de verrouiller un objet "encadré" dans une application C#, n'est-ce pas possible?Filetage C# - Verrouiller l'objet

class t 
    { 
     System.Object t_x = new object(); 

     public t(int p) 
     { 
      t_x = p; 

     } 

     public void w() 
     { 
      lock (t_x) 
      { 
       for (int i = 0; i < 4; i++) 
       { 

        { 
         t_x = ((int)t_x) + 1; 
         Console.WriteLine(t_x); 
         Thread.Sleep(1000); 
        } 
       } 
      } 
     } 
    } 

Dans une autre classe, je peux commencer à 2 fils:

 Thread b1 = new Thread(new ThreadStart(t1.w)); 
     b1.Start(); 
     Thread b2 = new Thread(new ThreadStart(t1.w)); 
     b2.Start(); 

Toutefois, la partie n'est pas fermée à clé. Lorsque je verrouille un objet arbitraire (c'est-à-dire créé et non modifié en tant qu'objet a = nouvel objet()), il se verrouille correctement. Est-ce que l'opération de boxe "dépromote" mon Object?

+2

Pourquoi voudriez-vous réattribuer l'objet de verrouillage dans un bloc de verrouillage? Cela va à l'encontre du but - puisque tout le point de la synchronisation est d'éviter que les threads diff ne se piétinent l'un l'autre pour une ressource "partagée". – Gishu

+0

Je pense que la réponse a accepté ce n'est pas le bon –

+0

Il semble y avoir un abus de langage récurrent chez les moins expérimentés essayant le filetage .Net que l'instruction lock verrouille réellement ou protège l'objet sur lequel le verrou est appelé. Ils semblent manquer le fait que l'objet est vraiment une clé pour un bloc de code verrouillé, la section critique. –

Répondre

16

Vous devez créer un objet de verrouillage distinct. Le problème est que vous réaffectez t_x à l'intérieur de la boucle. En supposant que le thread b1 entre dans la boucle avant que b2 n'atteigne l'instruction lock, b2 sera autorisé dans l'instruction lock car, à ce moment-là, t_x sera un nouvel objet qui n'a pas de verrou dessus.

+0

Je pense que c'est un meilleur moyen de faire cela –

+4

@Cedric - lire la réponse de Lee plus attentivement. L'instruction 'lock' est exactement la même que l'utilisation de' Monitor.Enter' et 'Monitor.Exit'. C'est juste une syntaxe plus propre pour le même motif. Le problème n'est pas d'utiliser 'lock' ou' Monitor' (pas vraiment un choix, car ils sont identiques). Le problème est de toujours passer le même objet à servir de verrou, qui est cassé si vous utilisez un type de valeur car il est mis en boîte différemment chaque fois que vous y accédez. –

23

Non, vous ne pouvez pas le faire - le bloc de verrouillage est un raccourci pour les éléments suivants:

try(Monitor.Enter(lockObject)) 
{ 
    //critical section 
} 
finally 
{ 
    Monitor.Exit(lockObject) 
} 

Les documentation for Monitor.Enter états, « Utiliser Monitor pour verrouiller des objets (qui est, les types de référence), pas les types de valeur Lorsque vous transmettez une variable de type valeur à Entrée, elle est encadrée comme un objet Si vous transmettez la même variable à Entrée à nouveau, elle est encadrée comme objet séparé et le thread ne bloque pas "

+0

Voilà la bonne façon de le faire. C'est dans le premier examen 70-536 de la certification Microsoft. –

+7

C'est la bonne réponse - pas à cause de l'extrait de code (l'instruction 'lock' est correcte) mais parce qu'elle mentionne la différence entre les types de valeur et de référence. Un type de valeur est de nouveau mis en boîte chaque fois qu'il est converti en la classe de base universelle 'object', de sorte qu'il ne peut pas agir de manière fiable en tant que cible de verrouillage. Donc, c'est mieux que la réponse acceptée. –

2

Vous devez utiliser un objet supplémentaire pour la serrure

object lockObj = new object(); 
public void foo() 
{ 
    lock(lockObj) 
    { 
    //do stuff here 
    } 
} 
2

L'appel lock (t_x) encadre un entier en tant qu'objet temporaire. Chaque appel à verrouiller (t_x) crée un nouvel objet et le verrouillage est inutile.

(verrouillage attend un objet et crée un objet temporaire NOUVEAU de l'entier)

il suffit de créer un objet de verrouillage séparé comme dit ci-dessus par Femaref.

0

Si vous voulez vraiment (? Besoin) pour verrouiller sur l'objet, vous pouvez utiliser une sorte d'emballage:

public class IntWrapper 
{ 
    public int Value{get;set;} 
} 

Ou si vous avez besoin de rester plus abstrait:

public class ObjectWrapper 
{ 
    public Object Value { get;set; } 
} 
+0

Bien que je ne recommanderais pas de le faire de cette façon (juste utiliser un autre 'object' pour verrouiller), je ne comprends pas les downvotes, car il * est * correct. –

0

Si vous voulez reconnaître quand les données sont chargées et si l'utilisation essaie de les utiliser avant cela, vous pouvez faire quelque chose comme ceci:

Avoir un drapeau booléen comme vous l'avez mentionné, mais utiliser un objet séparé pour le verrouiller avant y accéder pour éviter les conditions de course croisées. Lorsque l'utilisateur essaie d'utiliser les données, s'il n'est pas chargé (vérifiez la variable), vous pouvez ajouter un autre gestionnaire d'événement à l'événement RunWorkerCompleted du worker, qui fera immédiatement ce que l'utilisateur veut quand les données sont chargées.

Exemple:

public class MyClass 
{ 
    private bool dataIsReady = false; 
    private object locker = new object(); 
    BackgroundWorker worker; 

    public void Begin() 
    { 
     worker = new BackgroundWorker(); 
     worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted); 
    } 

    public void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
    { 
     lock (locker) 
     { 
      dataIsReady = true; 
     } 
    } 

    public void UseTriesToUseData() 
    { 
     lock (locker) 
     { 
      if (dataIsReady) 
      { 
       DoStuff(); 
      } 
      else 
      { 
       this.worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(DoStuffCaller); 
      } 
     } 
    } 

    private void DoStuff() 
    { 
     // Do stuff with data. 
    } 

    private void DoStuffCaller(object sender, RunWorkerCompletedEventArgs e) 
    { 
     this.DoStuff(); 
    } 
}