2008-10-05 5 views
1

Dans le code multithread, lorsqu'une instance peut être lue ou écrite par plusieurs threads, elle doit être verrouillée pour effectuer ces opérations en toute sécurité.Faciliter le verrouillage dans les MTA

Pour éviter la répétition de la création d'un objet sur lequel verrouiller et écrire un tas d'instructions de verrouillage par le code, j'ai créé une classe générique pour gérer le verrouillage.

Est-ce que quelque chose me manque, conceptuellement? Cela devrait fonctionner, non?

public class Locked<T> where T : new() 
{ 
    private readonly object locker = new object(); 

    private T value; 

    public Locked() 
     : this(default(T)) 
    { } 

    public Locked(T value) 
    { 
     this.value = value; 
    } 

    public T Get() 
    { 
     lock (this.locker) 
     { 
      return this.value; 
     } 
    } 

    public void Set(T value) 
    { 
     lock (this.locker) 
     { 
      this.value = value; 
     } 
    }  
} 

Et un exemple étant utilisé dans une classe:

private Locked<bool> stopWorkerThread = new Locked<bool>(); 

public void WorkerThreadEntryPoint() 
{ 
    while (true) 
    { 
     if (this.stopWorkerThread.Get()) 
     { 
      break; 

} 

Aussi, comment pourrais-je tester quelque chose comme ça, de manière automatisée (par exemple créer un test unitaire)?

Enfin, que puis-je faire pour mettre en œuvre un ++ et - opérateur, pour éviter ceci:

 this.runningThreads.Set(this.runningThreads.Get() + 1); 

Répondre

3

que seuls les verrous pendant toute la durée du get/set; bien sûr, dans de nombreux cas courants, cela sera atomique de toute façon, simplement en raison de la taille des données. Cependant, en réalité, la plupart des verrous doivent être plus longs, de la même manière que les collections qui ne verrouillent que l'Add etc. n'aident pas beaucoup - un appelant a généralement besoin d'un seul verrou pour le "est-il là?" si c'est le cas, ajoutez "sequence". Pour quelque chose d'aussi simple qu'un bool, "volatile" pourrait résoudre le problème beaucoup plus simplement - surtout si c'est juste pour une sortie de boucle. Vous pouvez également envisager [MethodImpl (MethodImplOptions.Synchronized)] - bien que personnellement je préfère un objet de verrou privé (comme vous l'avez utilisé) pour éviter les problèmes de verrouillage des personnes externes sur l'objet (ci-dessus utilise "this" comme la serrure). Pour les tests unitaires, vous aurez besoin de quelque chose pour prouver qu'il est cassé en premier - ce qui serait difficile car les opérations sont si petites (et déjà atomiques pour la plupart des types de données). Une des autres choses qu'il évite (que les correctifs volatils aussi) est la mise en cache dans un registre, mais encore une fois c'est une optimisation et difficile à forcer à prouver qu'il est cassé.

Si vous êtes intéressé par un verrou-wrapper, vous pouvez considérer le code existant comme this.

1

Votre code ci-dessus a un certain nombre de problèmes potentiels et réels de multi-threading, et je ne voudrais pas utiliser quelque chose comme ça dans une situation réelle. Par exemple:

this.runningThreads.Set(this.runningThreads.Get() + 1); 

Il existe une condition de concurrence assez évidente ici. Lorsque l'appel Get() renvoie, l'objet n'est plus verrouillé. Pour effectuer un véritable post ou pré-incrément, le compteur doit être verrouillé avant le Get et après le Set.

De même, vous n'avez pas toujours besoin de faire un verrou complet si tout ce que vous faites est synchrone.

Une meilleure interface de verrouillage nécessiterait (je pense) que vous verrouilliez explicitement l'instance où vous devez le faire.Mon expérience est principalement avec C++ je ne peux pas recommander une mise en œuvre complète, mais ma syntaxe préférée pourrait ressembler à ceci:

using (Locked<T> lock = Locked<T>(instance)) 
{ 
    // write value 
    instance++; 
} 

// read value 
print instance; 
+0

Le code exemple ne fonctionnerait pas à moins que le Verrouillé (instance) a été déclarée ailleurs et réutilisé pour tous les appelants - sinon chaque appelant a un verrou différent, ce qui va à l'encontre du but recherché. Et puis il serait plus simple de simplement déclarer un objet et de le verrouiller (obj) –

+0

Eh bien, j'étais en train de laisser les détails d'implémentation de la classe Locked <> en l'air, mais je pense que dans ce cas le verrou serait associé au instance, plutôt que la classe Locked <> - cela rend la classe Locked <> intrusive –