Frage

Also habe ich eine Menge Artikel jetzt gesehen habe behauptet, dass auf C ++ doppelt geprüft Verriegelung verwendet, um häufig mehrere Threads zu verhindern versucht, eine träge erstellt Singleton zu initialisieren, ist gebrochen. Normaler doppelt geprüft Sperrcode lautet wie folgt:

class singleton {
private:
    singleton(); // private constructor so users must call instance()
    static boost::mutex _init_mutex;

public:
    static singleton & instance()
    {
        static singleton* instance;

        if(!instance)
        {
            boost::mutex::scoped_lock lock(_init_mutex);

            if(!instance)           
                instance = new singleton;
        }

        return *instance;
    }
};

Das Problem ist offenbar die Linie Instanz zuweisen - der Compiler frei ist, das Objekt zuweisen und dann den Zeiger zuweisen, oder den Zeiger zu setzen, wo sie zugeteilt werden, dann zuordnen. Der letztere Fall bricht das Idiom - ein Thread kann den Speicher zuweisen und den Zeiger zuweisen, aber nicht die Singletons Konstruktor ausgeführt werden, bevor es schlafen gelegt wird - dann wird der zweite Thread wird sehen, dass die Instanz nicht null ist und versuchen Sie es zurück , auch wenn es bisher noch nicht aufgebaut hat.

sah ein Vorschlag einen Thread zu verwenden, lokale boolean und prüfen, ob statt instance. So etwas wie folgt aus:

class singleton {
private:
    singleton(); // private constructor so users must call instance()
    static boost::mutex _init_mutex;
    static boost::thread_specific_ptr<int> _sync_check;

public:
    static singleton & instance()
    {
        static singleton* instance;

        if(!_sync_check.get())
        {
            boost::mutex::scoped_lock lock(_init_mutex);

            if(!instance)           
                instance = new singleton;

            // Any non-null value would work, we're really just using it as a
            // thread specific bool.
            _sync_check = reinterpret_cast<int*>(1);
        }

        return *instance;
    }
};

Auf diese Weise jeden Thread Prüfung endet, wenn die Instanz einmal erstellt wurde, bleibt aber nach dem, was einige Leistungseinbußen zur Folge, aber immer noch nicht annähernd so schlimm wie jeden Anruf sperren. Aber was, wenn wir früher nur eine lokale static bool:

class singleton {
private:
    singleton(); // private constructor so users must call instance()
    static boost::mutex _init_mutex;

public:
    static singleton & instance()
    {
        static bool sync_check = false;
        static singleton* instance;

        if(!sync_check)
        {
            boost::mutex::scoped_lock lock(_init_mutex);

            if(!instance)           
                instance = new singleton;

            sync_check = true;
        }

        return *instance;
    }
};

Warum sollte das nicht? Selbst wenn sync_check von einem Thread zu lesen war, wenn es in einer anderen Wert des Mülls zugeordnet ist noch ungleich null und somit wahr sein. diesem Artikel Dr. Dobb behauptet, dass Sie sperren, weil Sie nie mit einem Kampf gewinnen der Compiler über Neuordnungs Anweisungen. Was macht ich denke, das aus irgendeinem Grunde nicht arbeiten müssen, aber ich kann nicht herausfinden, warum. Wenn die Anforderungen an den Sequenzpunkten sind als verlieren, wie der Artikel Dr. Dobb macht, glauben Sie mich, ich verstehe nicht, warum jeder Code nach der Sperre nicht mehr nachbestellt werden kann, bevor die Sperre zu sein. Welche gebrochene Periode C ++ Multithreading würde.

Ich glaube, ich der Compiler ausdrücklich erlaubt wird, sehen konnte sync_check neu anordnen, bevor das Schloss sein, weil es sich um eine lokale Variable ist (und auch wenn es statisch wir nicht einen Verweis oder Zeiger auf sie zurückkehrt) - aber dann könnte dies nach wie vor, indem es ein statisches Element gelöst werden (effektiv global) statt.

So wird diese Arbeit oder nicht wahr? Warum?

War es hilfreich?

Lösung

Sie fix behebt nichts, da die Schreibvorgänge auf sync_check und kann beispielsweise aus, um auf der CPU durchgeführt werden. Als ein Beispiel vorstellen, dass die ersten beiden Anrufe Beispiel etwa zur gleichen Zeit an zwei verschiedenen CPUs passieren. Der erste Thread das Lock erwerben, initialisieren und um den Zeiger gesetzt sync_check auf wahr, in dieser Reihenfolge, aber der Prozessor kann die Reihenfolge der Schreibvorgänge in den Speicher ändern. Auf der anderen CPU dann ist es möglich, dass der zweite Thread sync_check zu überprüfen, dass es wahr ist, aber beispielsweise noch nicht kann in dem Speicher geschrieben werden. Siehe Lockless Überlegungen zur Programmierung für die Xbox 360 und Microsoft Windows für Details.

Der Thread spezifische sync_check Lösung Sie sollten dann erwähnen arbeiten (vorausgesetzt, Sie initialisieren den Zeiger auf 0).

scroll top