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_variable
warten 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.wait
allein) 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_variable
Benutzeroberfläche lässt den Programmierer alles daran setzen, die Dinge zum Laufen zu bringen. Es gibt eine mögliche Nullzeiger-Dereferenzierung, wenn lk
versehentlich kein Mutex referenziert wird. Und es gibt keine Möglichkeit condition_variable::wait
zu überprüfen, ob dieser Thread die Sperre besitzt mut
.
Oh, nur daran erinnert, es besteht auch die Gefahr, dass der Programmierer die falsche unique_lock
Elementfunktion 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_variable
API 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
wait
Funktion 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::wait
nimmt 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_guard
Anwendungsfall / der Anweisung überein.
Wir brauchten unique_lock
sowieso, 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_lock
war 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::wait
Vorlage erstellt, damit ich einen beliebigen Lockable
Typ 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_any
erfordert ein condition_variable
Datenelement und ein shared_ptr<mutex>
Datenelement.
Da diese neue Funktionalität teurer ist als Ihre Basisfunktionalität condition_variable::wait
und ein condition_variable
so 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.