In C ++ 17 wurde eine neue Sperrklasse namens eingeführt std::scoped_lock
.
Aus der Dokumentation geht hervor, dass es der bereits vorhandenen std::lock_guard
Klasse ähnelt .
Was ist der Unterschied und wann sollte ich es verwenden?
In C ++ 17 wurde eine neue Sperrklasse namens eingeführt std::scoped_lock
.
Aus der Dokumentation geht hervor, dass es der bereits vorhandenen std::lock_guard
Klasse ähnelt .
Was ist der Unterschied und wann sollte ich es verwenden?
Antworten:
Dies scoped_lock
ist eine streng überlegene Version lock_guard
, die eine beliebige Anzahl von Mutexen gleichzeitig sperrt (unter Verwendung des gleichen Deadlock-Vermeidungsalgorithmus wie std::lock
). In neuem Code sollten Sie immer nur verwenden scoped_lock
.
Der einzige Grund, der lock_guard
noch besteht, ist die Kompatibilität. Es konnte nicht einfach gelöscht werden, da es im aktuellen Code verwendet wird. Darüber hinaus erwies es sich als unerwünscht, seine Definition zu ändern (von unär zu variadisch), da dies auch eine beobachtbare und damit brechende Änderung ist (jedoch aus etwas technischen Gründen).
lock_guard
. Aber es macht die Wachklassen sicherlich ein bisschen einfacher zu benutzen.
Der einzige und wichtige Unterschied besteht darin, dass std::scoped_lock
ein variadischer Konstruktor mehr als einen Mutex verwendet. Dies ermöglicht es, mehrere Mutexe in einem Deadlock zu sperren und so zu vermeiden, als ob std::lock
sie verwendet würden.
{
// safely locked as if using std::lock
std::scoped_lock<std::mutex, std::mutex> lock(mutex1, mutex2);
}
Zuvor mussten Sie ein wenig tanzen, um mehrere Mutexe auf sichere Weise zu sperren, indem Sie diese Antwortstd::lock
wie erläutert verwenden .
Das Hinzufügen einer Oszilloskopsperre erleichtert die Verwendung und vermeidet die damit verbundenen Fehler. Sie können als std::lock_guard
veraltet betrachten. Der Einzelargumentfall vonstd::scoped_lock
kann als Spezialisierung implementiert werden, sodass Sie sich nicht vor möglichen Leistungsproblemen fürchten müssen.
GCC 7 hat bereits Unterstützung, std::scoped_lock
die hier zu sehen ist .
Weitere Informationen finden Sie im Standardpapier
scoped_lock lk; // locks all mutexes in scope
. LGTM.
scoped_lock lk;
ist die neue Abkürzung für scoped_lock<> lk;
. Es gibt keine Mutexe. Du hast also recht. ;-)
Späte Antwort und meistens als Antwort auf:
Sie können als
std::lock_guard
veraltet betrachten.
Für den allgemeinen Fall, dass man genau einen Mutex sperren muss, std::lock_guard
gibt es eine API, die etwas sicherer zu verwenden ist als scoped_lock
.
Beispielsweise:
{
std::scoped_lock lock; // protect this block
...
}
Das obige Snippet ist wahrscheinlich ein versehentlicher Laufzeitfehler, da es kompiliert und dann absolut nichts tut. Der Codierer meinte wahrscheinlich:
{
std::scoped_lock lock{mut}; // protect this block
...
}
Jetzt wird es gesperrt / entsperrt mut
.
Wenn lock_guard
stattdessen in den beiden obigen Beispielen verwendet wurde, ist das erste Beispiel ein Kompilierungsfehler anstelle eines Laufzeitfehlers, und das zweite Beispiel hat die gleiche Funktionalität wie die verwendete Version scoped_lock
.
Mein Rat ist also, das einfachste Werkzeug für den Job zu verwenden:
lock_guard
Wenn Sie genau 1 Mutex für einen gesamten Bereich sperren müssen.
scoped_lock
Wenn Sie eine Anzahl von Mutexen sperren müssen, ist dies nicht genau 1.
unique_lock
wenn Sie im Rahmen des Blocks entsperren müssen (einschließlich der Verwendung mit a condition_variable
).
Dieser Ratschlag bedeutet nicht , dass er scoped_lock
neu gestaltet werden sollte, um keine 0 Mutexe zu akzeptieren. Es gibt gültige Anwendungsfälle, in denen es wünschenswert ist scoped_lock
, variable Vorlagenparameterpakete zu akzeptieren, die möglicherweise leer sind. Und der leere Koffer sollte nichts verriegeln.
Und deshalb lock_guard
ist es nicht veraltet. scoped_lock
und unique_lock
mag eine Obermenge der Funktionalität von sein lock_guard
, aber diese Tatsache ist ein zweischneidiges Schwert. Manchmal ist es genauso wichtig, was ein Typ nicht kann (in diesem Fall Standardkonstrukt).
Hier ist ein Beispiel und ein Zitat aus C ++ Concurrency in Action :
friend void swap(X& lhs, X& rhs)
{
if (&lhs == & rhs)
return;
std::lock(lhs.m, rhs.m);
std::lock_guard<std::mutex> lock_a(lhs.m, std::adopt_lock);
std::lock_guard<std::mutex> lock_b(rhs.m, std::adopt_lock);
swap(lhs.some_detail, rhs.some_detail);
}
vs.
friend void swap(X& lhs, X& rhs)
{
if (&lhs == &rhs)
return;
std::scoped_lock guard(lhs.m, rhs.m);
swap(lhs.some_detail, rhs.some_detail);
}
Das Vorhandensein von
std::scoped_lock
bedeutet, dass die meisten Fälle, die Siestd::lock
vor c ++ 17 verwendet hätten, jetztstd::scoped_lock
mit weniger Fehlerpotential geschrieben werden können, was nur eine gute Sache sein kann!