Ein Beispiel in Meyers Buch Effektive Moderne C++, Punkt 16.Ist meine Double-Checked Locking Pattern-Implementierung richtig?
in einer Klasse-Caching ein teurer zu berechnen int, könnten Sie versuchen, ein Paar std :: Atom avriables verwenden statt ein Mutex:
class Widget {
public:
int magicValue() const {
if (cachedValid) {
return cachedValue;
} else {
auto val1 = expensiveComputation1();
auto val2 = expensiveComputation2();
cachedValue = va1 + val2;
cacheValid = true;
return cachedValue;
}
}
private:
mutable std::atomic<bool> cacheValid { false };
mutable std::atomic<int> cachedValue;
};
Dies funktioniert, aber manchmal wird es viel härter zu arbeiten, als es should.Consider: ein Thread Widget ruft :: magicValue, sieht cacheValid als false, führt die zwei teuren Berechnungen durch und ordnet ihre Summe CachedValud zu. An diesem Punkt, ein zweiter Thread calss Widget :: magicValue, sieht auch cacheValid als falsch, und führt somit aus den gleichen teuren Berechnungen, die der erste Thread nur abgeschlossen hat.
Dann gibt er eine Lösung mit Mutex:
class Widget {
public:
int magicValue() const {
std::lock_guard<std::mutex> guard(m);
if (cacheValid) {
return cachedValue;
} else {
auto val1 = expensiveComputation1();
auto val2 = expensiveComputation2();
cachedValue = va1 + val2;
cacheValid = true;
return cachedValue;
}
}
private:
mutable std::mutex m;
mutable bool cacheValid { false };
mutable int cachedValue;
};
Aber ich glaube, die Lösung nicht so effecient ist, halte ich Mutex zu kombinieren und Atom bilden ein Doppel-Karomuster Sperren als unten.
class Widget {
public:
int magicValue() const {
if (!cacheValid) {
std::lock_guard<std::mutex> guard(m);
if (!cacheValid) {
auto val1 = expensiveComputation1();
auto val2 = expensiveComputation2();
cachedValue = va1 + val2;
cacheValid = true;
}
}
return cachedValue;
}
private:
mutable std::mutex m;
mutable std::atomic<bool> cacheValid { false };
mutable std::atomic<int> cachedValue;
};
Weil ich ein Neuling in Multi-Thread-Programmierung bin, so möchte ich wissen:
- Ist mein Code richtig?
- Ist die Leistung besser?
EDIT:
den Code behoben. (! CachedValue) wenn -> if (cacheValid!)
Im Prinzip ja, sind Sie hier richtig. Aber wenn wir davon ausgehen, dass die Berechnungen viel länger dauern als das Setzen der Atomics, ist die Wahrscheinlichkeit für Ihr Szenario sehr gering und die Verwendung der Atomics vermeidet teure Mutexe. –
Ich denke, Ihr Ansatz ist korrekt und besser als die beiden vorherigen. Es erklärte nur das * Double-Checked Locking Pattern *. – Lingxi
Ich glaube nicht, dass es korrekt ist, wenn ein zweiter Thread die cacheValid nach der ersten ausgewertet hat, aber bevor der Guard instanziiert wird. Ich denke, die Auswirkung kann gleich dem ersten Beispiel sein. – HappyCactus