std::scoped_lock
| Definiert in Header <mutex> |
||
| template< class... MutexTypes > class scoped_lock; |
(seit C++17) | |
Die Klasse scoped_lock ist ein Mutex-Wrapper, der einen bequemen RAII-Stil Mechanismus für den Besitz von null oder mehr Mutexen für die Dauer eines gesperrten Blocks bietet.
Wenn ein scoped_lock Objekt erstellt wird, versucht es, den Besitz der übergebenen Mutexe zu erlangen. Wenn die Kontrolle den Gültigkeitsbereich verlässt, in dem das scoped_lock Objekt erstellt wurde, wird das scoped_lock zerstört und die Mutexe werden freigegeben. Wenn mehrere Mutexe übergeben werden, wird ein Algorithmus zur Vermeidung von Deadlocks verwendet, als ob er von std::lock stammen würde.
Die Klasse scoped_lock ist nicht kopierbar.
Inhalt |
[bearbeiten] Template-Parameter
| MutexTypes | - | die Typen der zu sperrenden Mutexe. Die Typen müssen die Lockable Anforderungen erfüllen, es sei denn, sizeof...(MutexTypes) == 1, in diesem Fall muss der einzige Typ BasicLockable erfüllen. |
[bearbeiten] Member-Typen
| Mitgliedertyp | Definition |
mutex_type(bedingt vorhanden) |
Wenn sizeof...(MutexTypes) == 1, ist der Member-Typ |
[bearbeiten] Member-Funktionen
konstruiert ein scoped_lock, optional sperrt die gegebenen Mutexe(öffentliche Member-Funktion) | |
zerstört das scoped_lock Objekt, gibt die zugrunde liegenden Mutexe frei(öffentliche Member-Funktion) | |
| operator= [gelöscht] |
nicht kopierbar (öffentliche Member-Funktion) |
[bearbeiten] Hinweise
Ein häufiger Anfängerfehler ist es, einer scoped_lock Variablen einen Namen zu "vergessen", z.B. std::scoped_lock(mtx); (was eine scoped_lock Variable namens mtx standardmäßig konstruiert) oder std::scoped_lock{mtx}; (was ein prvalue-Objekt konstruiert, das sofort zerstört wird), wodurch kein Lock erstellt wird, das einen Mutex für den Rest des Gültigkeitsbereichs hält.
| Feature-Test-Makro | Wert | Std | Feature |
|---|---|---|---|
__cpp_lib_scoped_lock |
201703L |
(C++17) | std::scoped_lock
|
[bearbeiten] Beispiel
Das folgende Beispiel verwendet std::scoped_lock, um Mutex-Paare ohne Deadlock zu sperren und im RAII-Stil zu arbeiten.
#include <chrono> #include <functional> #include <iostream> #include <mutex> #include <string> #include <thread> #include <vector> using namespace std::chrono_literals; struct Employee { std::vector<std::string> lunch_partners; std::string id; std::mutex m; Employee(std::string id) : id(id) {} std::string partners() const { std::string ret = "Employee " + id + " has lunch partners: "; for (int count{}; const auto& partner : lunch_partners) ret += (count++ ? ", " : "") + partner; return ret; } }; void send_mail(Employee&, Employee&) { // Simulate a time-consuming messaging operation std::this_thread::sleep_for(1s); } void assign_lunch_partner(Employee& e1, Employee& e2) { static std::mutex io_mutex; { std::lock_guard<std::mutex> lk(io_mutex); std::cout << e1.id << " and " << e2.id << " are waiting for locks" << std::endl; } { // Use std::scoped_lock to acquire two locks without worrying about // other calls to assign_lunch_partner deadlocking us // and it also provides a convenient RAII-style mechanism std::scoped_lock lock(e1.m, e2.m); // Equivalent code 1 (using std::lock and std::lock_guard) // std::lock(e1.m, e2.m); // std::lock_guard<std::mutex> lk1(e1.m, std::adopt_lock); // std::lock_guard<std::mutex> lk2(e2.m, std::adopt_lock); // Equivalent code 2 (if unique_locks are needed, e.g. for condition variables) // std::unique_lock<std::mutex> lk1(e1.m, std::defer_lock); // std::unique_lock<std::mutex> lk2(e2.m, std::defer_lock); // std::lock(lk1, lk2); { std::lock_guard<std::mutex> lk(io_mutex); std::cout << e1.id << " and " << e2.id << " got locks" << std::endl; } e1.lunch_partners.push_back(e2.id); e2.lunch_partners.push_back(e1.id); } send_mail(e1, e2); send_mail(e2, e1); } int main() { Employee alice("Alice"), bob("Bob"), christina("Christina"), dave("Dave"); // Assign in parallel threads because mailing users about lunch assignments // takes a long time std::vector<std::thread> threads; threads.emplace_back(assign_lunch_partner, std::ref(alice), std::ref(bob)); threads.emplace_back(assign_lunch_partner, std::ref(christina), std::ref(bob)); threads.emplace_back(assign_lunch_partner, std::ref(christina), std::ref(alice)); threads.emplace_back(assign_lunch_partner, std::ref(dave), std::ref(bob)); for (auto& thread : threads) thread.join(); std::cout << alice.partners() << '\n' << bob.partners() << '\n' << christina.partners() << '\n' << dave.partners() << '\n'; }
Mögliche Ausgabe
Alice and Bob are waiting for locks Alice and Bob got locks Christina and Bob are waiting for locks Christina and Alice are waiting for locks Dave and Bob are waiting for locks Dave and Bob got locks Christina and Alice got locks Christina and Bob got locks Employee Alice has lunch partners: Bob, Christina Employee Bob has lunch partners: Alice, Dave, Christina Employee Christina has lunch partners: Alice, Bob Employee Dave has lunch partners: Bob
[bearbeiten] Fehlerberichte
Die folgenden Verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.
| DR | angewendet auf | Verhalten wie veröffentlicht | Korrigiertes Verhalten |
|---|---|---|---|
| LWG 2981 | C++17 | Ein redundanter Ableitungsführer von scoped_lock<MutexTypes...> wurde bereitgestellt |
entfernt |
[bearbeiten] Siehe auch
| (C++11) |
implementiert verschiebbaren Mutex-Besitz-Wrapper (Klassenvorlage) |
| (C++11) |
implementiert einen streng bereichsbezogenen Mutex-Besitz-Wrapper (Klassen-Template) |