Question

J'ai une classe singleton C# que plusieurs classes utilisent.L'accès est-il via Instance au Toggle() méthode thread-safe?Si oui, selon quelles hypothèses, règles, etc.Si non, pourquoi et comment puis-je le réparer ?

public class MyClass
{
    private static readonly MyClass instance = new MyClass();

    public static MyClass Instance
    {
        get { return instance; }
    }

    private int value = 0;

    public int Toggle()
    {
        if(value == 0) 
        {
            value = 1; 
        }
        else if(value == 1) 
        { 
            value = 0; 
        }

        return value;
    }
}
Était-ce utile?

La solution

L'accès via « Instance » à la classe « Toggle() » est-il thread-safe ?Si oui, selon quelles hypothèses, règles, etc.Si non, pourquoi et comment puis-je y remédier ?

Non, ce n'est pas threadsafe.

Fondamentalement, les deux threads peuvent exécuter le Toggle fonctionner en même temps, donc cela pourrait arriver

    // thread 1 is running this code
    if(value == 0) 
    {
        value = 1; 
        // RIGHT NOW, thread 2 steps in.
        // It sees value as 1, so runs the other branch, and changes it to 0
        // This causes your method to return 0 even though you actually want 1
    }
    else if(value == 1) 
    { 
        value = 0; 
    }
    return value;

Vous devez fonctionner avec l'hypothèse suivante.

Si 2 threads sont en cours d’exécution, ils peuvent s’entrelacer et interagir de manière aléatoire à tout moment.Vous pouvez être à mi-chemin de l'écriture ou de la lecture d'un entier ou d'un flottant de 64 bits (sur un processeur 32 bits) et un autre thread peut intervenir et le modifier sous vous.

Si les 2 threads n'accèdent jamais à rien de commun, ce n'est pas grave, mais dès qu'ils le font, vous devez les empêcher de se marcher sur les pieds.La façon de procéder dans .NET consiste à utiliser des verrous.

Vous pouvez décider quoi et où verrouiller en pensant à des choses comme ceci :

Pour un bloc de code donné, si la valeur de something j'ai été changé sous moi, est-ce important ?Si c'est le cas, vous devez le verrouiller something pour la durée du code là où cela serait important.

En regardant à nouveau votre exemple

    // we read value here
    if(value == 0) 
    {
        value = 1; 
    }
    else if(value == 1) 
    { 
        value = 0; 
    }
    // and we return it here
    return value;

Pour que cela renvoie ce que nous attendons, nous supposons que value ne sera pas modifié entre la lecture et le return.Pour que cette hypothèse soit réellement correcte, vous devez verrouiller value pendant la durée de ce bloc de code.

Donc tu ferais ceci :

lock( value )
{
     if(value == 0) 
     ... // all your code here
     return value;
}

CEPENDANT

Dans .NET, vous ne pouvez verrouiller que les types de référence.Int32 est un type valeur, nous ne pouvons donc pas le verrouiller.
Nous résolvons ce problème en introduisant un objet « factice » et en verrouillant que partout où nous voudrions verrouiller la « valeur ».

C'est quoi Ben Scheirman fait référence à.

Autres conseils

L'implémentation originale n'est pas thread-safe, comme le souligne Ben

Un moyen simple de le rendre thread-safe consiste à introduire une instruction lock.Par exemple.comme ça:

public class MyClass
{
    private Object thisLock = new Object();
    private static readonly MyClass instance = new MyClass();
    public static MyClass Instance
    {
        get { return instance; }
    }
    private Int32 value = 0;
    public Int32 Toggle()
    {
        lock(thisLock)
        {
            if(value == 0) 
            {
                value = 1; 
            }
            else if(value == 1) 
            { 
                value = 0; 
            }
            return value;
        }
    }
}

C'est ce que je pensais.Mais, je cherche les détails ...'Toggle ()' n'est pas une méthode statique, mais elle est un membre d'une propriété statique (lorsque vous utilisez «instance»).Est-ce ce qui le fait partager entre les fils?

Si votre application est multithread et que vous pouvez prévoir que plusieurs threads accéderont à cette méthode, cela la rend partagée entre les threads.Parce que votre classe est un Singleton, vous savez que le thread différent accédera au MÊME objet, soyez donc prudent quant à la sécurité des threads de vos méthodes.

Et comment cela s'applique-t-il aux singletons en général.Dois-je y remédier dans chaque méthode de ma classe?

Comme je l'ai dit ci-dessus, parce qu'il s'agit d'un singleton, vous savez que différents threads accéderont au même objet, éventuellement en même temps.Cela ne signifie pas que vous devez faire en sorte que chaque méthode obtienne un verrou.Si vous remarquez qu'un appel simultané peut conduire à un état corrompu de la classe, vous devez alors appliquer la méthode mentionnée par @Thomas.

Puis-je supposer que le modèle singleton expose ma belle classe thread-safe à tous les problèmes de thread des membres statiques réguliers ?

Non.Votre classe n'est tout simplement pas threadsafe.Le singleton n’a rien à voir là-dedans.

(Je comprends que les membres de l'instance appelés sur un objet statique provoquent des problèmes de thread)

Cela n'a rien à voir non plus.

Il faut penser ainsi :Est-il possible dans mon programme que 2 threads (ou plus) accèdent à cette donnée en même temps ?

Le fait que vous obteniez les données via un singleton ou une variable statique, ou que vous transmettiez un objet en tant que paramètre de méthode n'a pas d'importance.En fin de compte, il ne s'agit que de quelques bits et octets dans la RAM de votre PC, et tout ce qui compte est de savoir si plusieurs threads peuvent voir les mêmes bits.

Votre thread pourrait s'arrêter au milieu de cette méthode et transférer le contrôle à un autre thread.Vous avez besoin d'une section critique autour de ce code...

private static object _lockDummy = new object();


...

lock(_lockDummy)
{
   //do stuff
}

J'ajouterais également un constructeur protégé à MyClass pour empêcher le compilateur de générer un constructeur public par défaut.

Je pensais que si je supprimais le modèle singleton et forçais tout le monde à obtenir une nouvelle instance de la classe, cela réduirait certains problèmes...mais cela n'empêche personne d'autre d'initialiser un objet statique de ce type et de le transmettre...ou en créant plusieurs threads, tous accédant à « Toggle() » à partir de la même instance.

Bingo :-)

Je comprends maintenant.C'est un monde difficile.J'aurais aimé ne pas refactoriser le code existant :(

Malheureusement, le multithreading est difficile et vous devez être très paranoïaque sur les choses :-) La solution la plus simple dans ce cas est de rester avec le singleton et d'ajouter une serrure autour de la valeur, comme dans les exemples.

Eh bien, en fait, je ne connais pas très bien C#...mais je suis ok en Java, donc je vais donner la réponse à cela, et j'espère que les deux sont suffisamment similaires pour que cela soit utile.Sinon, je m'en excuse.

La réponse est non, ce n’est pas sûr.Un thread pourrait appeler Toggle() en même temps que l'autre, et il est possible, bien que peu probable avec ce code, que Thread1 puisse définir value entre le moment où Thread2 le vérifie et le moment où il le définit.

Pour résoudre le problème, faites simplement Toggle() synchronized.Il ne bloque rien et n'appelle rien qui pourrait générer un autre thread qui pourrait appeler Toggle(), c'est donc tout ce que vous avez à faire, enregistrez-le.

Citation:

if(value == 0) { value = 1; }
if(value == 1) { value = 0; }
return value;

value sera toujours 0...

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top