Question

Je suis en train de refactoriser du code et je m'interroge sur l'utilisation d'un lock dans le constructeur d'instance.

public class MyClass {

    private static Int32 counter = 0;
    private Int32 myCount;

    public MyClass() {

        lock(this) {
            counter++;
            myCount = counter;
        }
    }
}

Veuillez confirmer

  1. Les constructeurs d'instances sont thread-safe.
  2. L'instruction lock empêche l'accès à ce bloc de code, pas au membre statique « counter ».

Si l'intention du programmeur d'origine était que chaque instance connaisse son « compte », comment synchroniser l'accès au membre « compteur » pour garantir qu'un autre thread ne crée pas de nouveau fichier ? MyClass et changer le décompte avant que celui-ci ne définisse son décompte ?

Pour info - Cette classe n'est pas un singleton.Les instances doivent simplement connaître leur nombre.

Était-ce utile?

La solution

@ajmastrean

Je ne dis pas que vous devriez utiliser le modèle singleton lui-même, mais adopter sa méthode d'encapsulation du processus d'instanciation.

c'est à dire.

  • Rendre le constructeur privé.
  • Créez une méthode d'instance statique qui renvoie le type.
  • Dans la méthode d'instance statique, utilisez le mot-clé lock avant d'instancier.
  • Instanciez une nouvelle instance du type.
  • Augmentez le nombre.
  • Déverrouillez et renvoyez la nouvelle instance.

MODIFIER

Un problème qui m'est venu à l'esprit : comment savoir quand le décompte a diminué ?;)

MODIFIER À NOUVEAU

En y réfléchissant, vous pourriez ajouter du code au destructeur qui appelle une autre méthode statique pour décrémenter le compteur :D

Autres conseils

Si vous incrémentez uniquement un nombre, il existe une classe spéciale (Interlocked) pour cela...

http://msdn.microsoft.com/en-us/library/system.threading.interlocked.increment.aspx

Méthode Interlocked.Increment

Incrémente une variable spécifiée et stocke le résultat, sous la forme d'une opération atomique.

System.Threading.Interlocked.Increment(myField);

Plus d’informations sur les meilleures pratiques en matière de threading...

http://msdn.microsoft.com/en-us/library/1c9txz50.aspx

Je suppose que c'est pour un modèle singleton ou quelque chose comme ça.Ce que vous voulez faire n'est pas verrouiller votre objet, mais verrouiller le compteur pendant que vous le modifiez.

private static int counter = 0;
private static object counterLock = new Object();

lock(counterLock) {
    counter++;
    myCounter = counter;
}

Parce que votre code actuel est en quelque sorte redondant.Surtout étant dans le constructeur où il n'y a qu'un seul thread qui peut appeler un constructeur, contrairement aux méthodes où il peut être partagé entre les threads et accessible à partir de n'importe quel thread partagé.

D'après le peu que je peux dire de votre code, vous essayez de donner à l'objet le nombre actuel au moment de sa création.Ainsi, avec le code ci-dessus, le compteur sera verrouillé pendant que le compteur sera mis à jour et défini localement.Tous les autres constructeurs devront donc attendre que le compteur soit libéré.

Vous pouvez utiliser un autre objet statique pour le verrouiller.

private static Object lockObj = new Object();

et verrouillez cet objet dans le constructeur.

lock(lockObj){}

Cependant, je ne suis pas sûr qu'il existe des situations qui devraient être gérées en raison de l'optimisation du compilateur dans .NET comme dans le cas de Java

La manière la plus efficace d’y parvenir serait d’utiliser l’opération d’incrémentation verrouillée.Il incrémentera le compteur et renverra la valeur nouvellement définie du compteur statique en même temps (atomiquement)

class MyClass {

    static int _LastInstanceId = 0;
    private readonly int instanceId; 

    public MyClass() { 
        this.instanceId = Interlocked.Increment(ref _LastInstanceId);  
    }
}

Dans votre exemple d'origine, l'instruction lock(this) n'aura pas l'effet souhaité car chaque instance individuelle aura une référence "this" différente, et plusieurs instances pourraient ainsi mettre à jour le membre statique en même temps.

Dans un sens, les constructeurs peuvent être considérés comme thread-safe car la référence à l'objet en cours de construction n'est pas visible tant que le constructeur n'a pas terminé, mais cela ne sert à rien pour protéger une variable statique.

(Mike Schall avait le mors verrouillé en premier)

Je pense que si vous modifiez le Modèle Singleton pour inclure un décompte (évidemment en utilisant la méthode thread-safe), tout ira bien :)

Modifier

Merde, j'ai accidentellement supprimé !

Je ne sais pas si les constructeurs d'instances SONT thread-safe, je me souviens avoir lu cela dans un livre de modèles de conception, vous devez vous assurer que les verrous sont en place pendant le processus d'instanciation, uniquement à cause de cela.

@Rob

Pour information, cette classe n'est peut-être pas un singleton, j'ai besoin d'accéder à différentes instances.Ils doivent simplement tenir un décompte.Quelle partie du modèle singleton modifieriez-vous pour effectuer une incrémentation de « compteur » ?

Ou suggérez-vous que j'expose une méthode statique de construction bloquant l'accès au code qui incrémente et lit le compteur avec un verrou.

public MyClass {

    private static Int32 counter = 0;
    public static MyClass GetAnInstance() {

        lock(MyClass) {
            counter++;
            return new MyClass();
        }
    }

    private Int32 myCount;
    private MyClass() {
        myCount = counter;
    }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top