Es gibt also keinen technischen Grund?
Ich habe die Antwort von cmeerw positiv bewertet, weil ich glaube, dass er einen technischen Grund angegeben hat. Lass uns durchgehen. Stellen wir uns vor, das Komitee hätte beschlossen, auf einen zu condition_variablewarten mutex. Hier ist Code mit diesem Design:
void foo()
{
mut.lock();
while (not_ready)
cv.wait(mut);
mut.unlock();
}
Genau so sollte man a nicht benutzen condition_variable. In den mit:
Es gibt ein Ausnahme-Sicherheitsproblem, und es ist ein ernstes. Wenn in diesen Bereichen (oder für sich cv.waitallein) eine Ausnahme ausgelöst wird , ist der gesperrte Status des Mutex durchgesickert, es sei denn, irgendwo wird ein Versuch / Fang eingefügt, um die Ausnahme abzufangen und zu entsperren. Aber das ist nur mehr Code, den der Programmierer schreiben soll.
Angenommen, der Programmierer weiß, wie ausnahmesicherer Code geschrieben und verwendet wird unique_lock, um dies zu erreichen. Jetzt sieht der Code so aus:
void foo()
{
unique_lock<mutex> lk(mut);
while (not_ready)
cv.wait(*lk.mutex());
}
Das ist viel besser, aber es ist immer noch keine großartige Situation. Die condition_variableBenutzeroberfläche lässt den Programmierer alles daran setzen, die Dinge zum Laufen zu bringen. Es gibt eine mögliche Nullzeiger-Dereferenzierung, wenn lkversehentlich kein Mutex referenziert wird. Und es gibt keine Möglichkeit condition_variable::waitzu überprüfen, ob dieser Thread die Sperre besitzt mut.
Oh, nur daran erinnert, es besteht auch die Gefahr, dass der Programmierer die falsche unique_lockElementfunktion wählt , um den Mutex freizulegen. *lk.release()wäre hier katastrophal.
Schauen wir uns nun an, wie der Code mit der tatsächlichen condition_variableAPI geschrieben wird, die Folgendes benötigt unique_lock<mutex>:
void foo()
{
unique_lock<mutex> lk(mut);
while (not_ready)
cv.wait(lk);
}
- Dieser Code ist so einfach wie möglich.
- Es ist ausnahmesicher.
- Die
waitFunktion kann lk.owns_lock()eine Ausnahme prüfen und auslösen, wenn dies der Fall ist false.
Dies sind technische Gründe, die das API-Design von vorangetrieben haben condition_variable.
Zusätzlich condition_variable::waitnimmt eine nicht lock_guard<mutex>da lock_guard<mutex>ist , wie Sie sagen: Ich besitze die Sperre auf diesem Mutex , bis lock_guard<mutex>zerstört. Wenn Sie jedoch anrufen condition_variable::wait, lösen Sie implizit die Sperre für den Mutex. Diese Aktion stimmt also nicht mit dem lock_guardAnwendungsfall / der Anweisung überein.
Wir brauchten unique_locksowieso, damit man Sperren von Funktionen zurückgeben, sie in Container packen und Mutexe in Mustern ohne Gültigkeitsbereich auf ausnahmesichere Weise sperren / entsperren konnte, also unique_lockwar dies die natürliche Wahl für condition_variable::wait.
Aktualisieren
Bambus schlug in den Kommentaren unten vor, dass ich kontrastiere condition_variable_any, also hier geht's:
Frage: Warum wird keine condition_variable::waitVorlage erstellt, damit ich einen beliebigen LockableTyp an ihn übergeben kann?
Antworten:
Das ist wirklich coole Funktionalität. In diesemshared_lock Artikel wird beispielsweise Code demonstriert, der auf einen (rwlock) im gemeinsam genutzten Modus auf eine Bedingungsvariable wartet (etwas, das in der Posix-Welt unbekannt ist, aber dennoch sehr nützlich). Die Funktionalität ist jedoch teurer.
Deshalb hat das Komitee einen neuen Typ mit dieser Funktionalität eingeführt:
`condition_variable_any`
Mit diesem condition_variable Adapter kann man auf jeden abschließbaren Typ warten . Wenn es Mitglieder hat lock()und unlock(), sind Sie gut zu gehen. Eine ordnungsgemäße Implementierung von condition_variable_anyerfordert ein condition_variableDatenelement und ein shared_ptr<mutex>Datenelement.
Da diese neue Funktionalität teurer ist als Ihre Basisfunktionalität condition_variable::waitund ein condition_variableso einfaches Tool ist, wurde diese sehr nützliche, aber teurere Funktionalität in eine separate Klasse eingeteilt, sodass Sie nur dann dafür bezahlen, wenn Sie sie verwenden.