Frage

Ich habe eine C#-Singleton-Klasse, die von mehreren Klassen verwendet wird.Ist der Zugriff durch Instance zum Toggle() Methode threadsicher?Wenn ja, nach welchen Annahmen, Regeln usw.Wenn nein, warum Und wie kann ich es reparieren?

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;
    }
}
War es hilfreich?

Lösung

Ist der Zugriff über „Instanz“ auf die Klasse „Toggle()“ threadsicher?Wenn ja, nach welchen Annahmen, Regeln usw.Wenn nein, warum und wie kann ich das Problem beheben?

Nein, es ist nicht threadsicher.

Grundsätzlich können beide Threads ausgeführt werden Toggle gleichzeitig funktionieren, so dass dies passieren könnte

    // 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;

Sie müssen mit der folgenden Annahme operieren.

Wenn 2 Threads laufen, können und werden sie sich an jedem Punkt zufällig verschachteln und miteinander interagieren.Sie können mit dem Schreiben oder Lesen einer 64-Bit-Ganzzahl oder Gleitkommazahl (auf einer 32-Bit-CPU) zur Hälfte fertig sein und ein anderer Thread kann unter Ihnen einspringen und sie ändern.

Wenn die beiden Threads nie auf etwas Gemeinsames zugreifen, spielt das keine Rolle, aber sobald dies der Fall ist, müssen Sie verhindern, dass sie sich gegenseitig auf die Füße treten.In .NET geht das mit Sperren.

Sie können entscheiden, was und wo Sie sperren möchten, indem Sie über Dinge wie diese nachdenken:

Für einen bestimmten Codeblock, wenn der Wert von something Wurde unter mir umgezogen, wäre das wichtig?Wenn ja, müssen Sie das sperren something für die Dauer des Codes, wo es wichtig wäre.

Schauen Sie sich Ihr Beispiel noch einmal an

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

Damit dies das zurückgibt, was wir erwarten, gehen wir davon aus value wird zwischen dem Lesen und dem nicht geändert return.Damit diese Annahme tatsächlich richtig ist, müssen Sie sperren value für die Dauer dieses Codeblocks.

Sie würden also Folgendes tun:

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

JEDOCH

In .NET können Sie nur Referenztypen sperren.Int32 ist ein Werttyp, daher können wir ihn nicht sperren.
Wir lösen dieses Problem, indem wir ein „Dummy“-Objekt einführen und sperren Das wo auch immer wir „Wert“ sperren möchten.

Das ist was Ben Scheirman bezieht sich auf.

Andere Tipps

Die ursprüngliche Implementierung ist nicht threadsicher, wie Ben betont

Eine einfache Möglichkeit, es Thread-sicher zu machen, besteht darin, eine Lock-Anweisung einzuführen.Z.B.so was:

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;
        }
    }
}

Das dachte ich mir.Aber ich suche nach den Details ..."Toggle ()" ist keine statische Methode, sondern ein Mitglied einer statischen Eigenschaft (bei Verwendung von "Instanz").Ist es das, was es unter Threads geteilt hat?

Wenn es sich bei Ihrer Anwendung um eine Multithread-Anwendung handelt und Sie davon ausgehen können, dass mehrere Threads auf diese Methode zugreifen, wird sie von den Threads gemeinsam genutzt.Da Ihre Klasse ein Singleton ist, wissen Sie, dass der andere Thread auf das GLEICHE Objekt zugreift. Seien Sie daher hinsichtlich der Thread-Sicherheit Ihrer Methoden vorsichtig.

Und wie gilt dies für Singletons im Allgemeinen.Müsste ich dies in jeder Methode in meiner Klasse ansprechen?

Wie ich oben sagte, wissen Sie, dass verschiedene Threads möglicherweise gleichzeitig auf dasselbe Objekt zugreifen, da es sich um einen Singleton handelt.Das bedeutet nicht, dass Sie dafür sorgen müssen, dass jede Methode eine Sperre erhält.Wenn Sie feststellen, dass ein gleichzeitiger Aufruf zu einem beschädigten Zustand der Klasse führen kann, sollten Sie die von @Thomas erwähnte Methode anwenden

Kann ich davon ausgehen, dass das Singleton-Muster meine ansonsten schöne Thread-sichere Klasse allen Thread-Problemen regulärer statischer Member aussetzt?

NEIN.Ihre Klasse ist einfach nicht threadsicher.Der Singleton hat damit nichts zu tun.

(Mir ist klar geworden, dass Instanzmitglieder, die für ein statisches Objekt aufgerufen werden, Threading-Probleme verursachen.)

Damit hat es auch nichts zu tun.

Man muss so denken:Ist es in meinem Programm möglich, dass zwei (oder mehr) Threads gleichzeitig auf diese Daten zugreifen?

Die Tatsache, dass Sie die Daten über einen Singleton oder eine statische Variable erhalten oder ein Objekt als Methodenparameter übergeben, spielt keine Rolle.Letztendlich sind es nur ein paar Bits und Bytes im RAM Ihres PCs, und alles, was zählt, ist, ob mehrere Threads dieselben Bits sehen können.

Ihr Thread könnte mitten in dieser Methode anhalten und die Kontrolle an einen anderen Thread übertragen.Sie benötigen einen kritischen Abschnitt um diesen Code ...

private static object _lockDummy = new object();


...

lock(_lockDummy)
{
   //do stuff
}

Ich würde MyClass auch einen geschützten Konstruktor hinzufügen, um zu verhindern, dass der Compiler einen öffentlichen Standardkonstruktor generiert.

Ich dachte, wenn ich das Singleton-Muster entsorge und alle dazu zwinge, eine neue Instanz der Klasse zu erhalten, würde das einige Probleme lindern ...aber das hält niemanden davon ab, ein statisches Objekt dieses Typs zu initialisieren und weiterzugeben ...oder durch die Ausgliederung mehrerer Threads, die alle von derselben Instanz aus auf „Toggle()“ zugreifen.

Bingo :-)

Ich verstehe es jetzt.Es ist eine harte Welt.Ich wünschte, ich würde alten Code nicht umgestalten :(

Leider ist Multithreading schwierig und Sie müssen in diesem Fall sehr paranoid sein :-) Die einfachste Lösung besteht darin, sich beim Singleton zu halten und wie in den Beispielen ein Schloss um den Wert hinzuzufügen.

Nun ja, ich kenne mich mit C# eigentlich nicht so gut aus ...Aber ich kenne mich gut mit Java aus, also werde ich die Antwort darauf geben, und hoffentlich sind die beiden ähnlich genug, dass es nützlich ist.Wenn nicht, entschuldige ich mich.

Die Antwort lautet: Nein, es ist nicht sicher.Ein Thread könnte Toggle() gleichzeitig mit dem anderen aufrufen, und es ist möglich, wenn auch mit diesem Code unwahrscheinlich, dass Thread1 gesetzt wird value zwischen den Zeiten, in denen Thread2 es überprüft, und wenn es es festlegt.

Um das Problem zu beheben, führen Sie einfach Toggle() aus. synchronized.Es blockiert nichts und ruft nichts auf, was einen anderen Thread erzeugen könnte, der Toggle() aufrufen könnte. Das ist also alles, was Sie tun müssen, um es zu speichern.

Zitat:

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

value wird immer 0 sein...

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top