Double-Checked-Lock Singleton in C++11

Ist die folgende singleton-Implementierung data-race free?

static std::atomic<Tp *> m_instance;
...

static Tp &
instance()
{
    if (!m_instance.load(std::memory_order_relaxed))
    {
        std::lock_guard<std::mutex> lock(m_mutex);
        if (!m_instance.load(std::memory_order_acquire))
        {
            Tp * i = new Tp;
            m_instance.store(i, std::memory_order_release);    
        }    
    }

    return * m_instance.load(std::memory_order_relaxed);
}

Ist die std::memory_model_acquire von der Last operation überflüssig? Ist es möglich, weiter zu entspannen, laden und store-Operationen durch Umschalten Sie std::memory_order_relaxed? In diesem Fall ist das erwerben/release-Semantik std::mutex genug, um Garantie für die Richtigkeit oder eine weitere std::atomic_thread_fence(std::memory_order_release) ist auch erforderlich, um zu gewährleisten, dass die Schreibvorgänge auf den Speicher der Konstruktor geschehen, bevor die entspannte speichern? Doch ist die Nutzung der Zaun entspricht das Geschäft mit memory_order_release?

BEARBEITEN: Dank der Antwort von John, ich kam mit der folgenden Implementierung, sollte die data-race-free. Auch wenn die innere Kraft nicht-atomaren überhaupt, habe ich beschlossen, zu verlassen, entspannter laden, dass es keinen Einfluss auf die Leistung. Im Vergleich zu immer einer äußeren Belastung mit dem Erwerb Speicher, um die thread_local Maschinen verbessert die Leistung der Zugriff auf die Instanz der etwa eine Größenordnung.

static Tp &
instance()
{
    static thread_local Tp *instance;

    if (!instance && 
        !(instance = m_instance.load(std::memory_order_acquire)))
    {
        std::lock_guard<std::mutex> lock(m_mutex);
        if (!(instance = m_instance.load(std::memory_order_relaxed)))
        {
            instance = new Tp; 
            m_instance.store(instance, std::memory_order_release);    
        }    
    }
    return *instance;
}
  • Ich sehe einen Fehler, dein code ist nicht das singleton-kostenlos.
  • Wenn Sie nicht wissen, wie zu implementieren eines Singleton sicher, dann sollten Sie wahrscheinlich nicht verwenden Sie eine.
  • Von Ihnen positiv bewertet werden. C++11 <atomic> ist ein neuer header mit der neuen Terminologie. Fragen, wie es zu benutzen, um sicher zu implementieren double-checked locking ist eine ausgezeichnete Frage, die ich mir vorstellen, werden viele Menschen sich Fragen.
  • Das double-checked-locking-Muster hat gezeigt, dass unsicher von Scott Meyers und Andrei Alexandrescu. Siehe aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf für details. Matthäus
  • Wo würde m_mutex definiert werden?
  • Gilman: das Papier bezieht sich speziell auf [15] ISO/IEC 14882: 1998
  • Wenn es die thread-lokalen, wie es ein singleton? Und wenn es die thread-lokalen und seine Adresse nicht herum, warum tun Sie müssen atomaren laden überhaupt? Aber ohne die thread_local es scheint gut zu sein.
  • Meiner Meinung nach, der gesamte Begriff der double-checked-locking als solche ist Quatsch. Der einzig sichere (und nicht Total wirr, unintellegible -) Weg zu erreichen, was double-checked locking versucht zu tun ist erstellen einer singleton aus dem Haupt-thread, bevor irgendwelche anderen threads erstellt werden. Trivial zu tun, keine komplizierten voodoo-Bedarf deutlich weniger Aufwand, und garantiert, um zu arbeiten.
  • Bonelli: Sie erkennen, dass der obige code mit thread_local ist nicht implementiert singleton-pattern? Ihre Instanzen werden mehrere, einer pro thread.
  • Nein werden Sie nicht, da alle threads teilen sich die gleiche Instanz über m_instance. Die thread_local instance ist nur ein cache für die Instanz, um zu vermeiden Erzeugung einer atomaren Belastung pro Zugang
  • Du hast Recht, ich habe übersehen, dass. Obwohl gegeben, wie sind Atomare liest implementiert auf den meisten Plattformen und wie ist thread_local umgesetzt GCC, derzeit, ich denke, es wäre besser zu tun, ohne die thread_local variable und gegen Atomare Lesen statt.

Schreibe einen Kommentar