Понятно ли, что произойдет, если поток, заблокированный на std::condition_variable, получит уведомление, но блокировка связанного мьютекса еще не снята, и блокировка будет снята через 10 секунд? Будет ли поток ждать освобождения блокировки или ситуация не определена? Я специально добавил 15-секундный спящий режим, чтобы сделать блокировку недоступной в течение этого времени, чтобы увидеть, как будет работать ожидающий поток. Все в порядке, но я просто хотел убедиться в этом.
#include <iostream>
#include <condition_variable>
#include <thread>
#include <chrono>
struct SharedResource
{
SharedResource() :
cv_mutex(), cv(), counter(0)
{
}
/*
This mutex is used for three purposes:
1) to synchronize accesses to counter
2) to synchronize accesses to std::cerr
3) for the condition variable cv
*/
std::mutex cv_mutex;
std::condition_variable cv;
int counter;
};
void waits(SharedResource& sharedRes)
{
std::unique_lock<std::mutex> lk(sharedRes.cv_mutex);
std::cerr << "Waiting... \n";
while (sharedRes.counter != 1)
{
sharedRes.cv.wait_for(lk,3s);
std::cerr << "Thread ID: " << std::this_thread::get_id() << " wakes up every 3 seconds.\n";
}
std::cerr << "...finished waiting." << "counter: " << sharedRes.counter << std::endl;
} //The lk object will be unlocked after this scope ends.
void signals(SharedResource& sharedRes)
{
std::this_thread::sleep_for(std::chrono::seconds(1));
{
std::lock_guard<std::mutex> lk(sharedRes.cv_mutex);
std::cerr << "Notifying...\n";
} // The lk object will be unlocked after this scope ends.
sharedRes.cv.notify_all();
std::this_thread::sleep_for(std::chrono::seconds(6));
{
std::lock_guard<std::mutex> lk(sharedRes.cv_mutex);
sharedRes.counter = 1;
std::cerr << "Notifying again...\n";
sharedRes.cv.notify_all();
std::this_thread::sleep_for(std::chrono::seconds(15));
}// The lk object will be unlocked after this scope ends.
}
int main()
{
SharedResource sharedRes;
std::thread
t1(waits, std::ref(sharedRes)),
t2(signals, std::ref(sharedRes));
t1.join();
t2.join();
}
Если поток, который заблокирован в std::condition_variable, уведомлен, но блокировка связанного с ним mutex не снята, что произойдет?
Он будет продолжать wait / wait_for до тех пор, пока не сможет снова получить блокировку.
Когда std::condition_variable::wait и wait_for возвращаются (по какой-либо причине), блокировка снова удерживается, поэтому вам не нужно об этом беспокоиться.
Он может даже вернуться из wait без каких-либо уведомлений (ложных пробуждений) - но, несмотря ни на что, блокировка будет восстановлена, когда вызов вернется.
Если я правильно помню, condition_variables могут ложно просыпаться, поэтому всегда используйте wait/wait_for с предикатом. Таким образом, условная переменная возобновит ожидание, если условие все еще не выполнено.
@PepijnKramer Да, ложные пробуждения реальны (как я уже упоминал в ответе), но даже если это причина возврата wait, блокировка удерживается, когда она возвращается.
Спасибо за ответ. Не могли бы вы уточнить это подробнее. Меня немного смущает следующее утверждение? что вы подразумеваете под «возвратом» в этом контексте? "Когда std::condition_variable::wait и wait_for возвращаются (по какой-либо причине), блокировка снова удерживается, так что вам не нужно об этом беспокоиться." " ... но, несмотря ни на что, блокировка повторно получена когда звонок вернется».
@Sam Это просто означает, что когда вы вызываете cv.wait или cv.wait_for, он «зависает», а когда он возвращается из зависания, блокировка снова получена.
@Sam Есть что-нибудь еще, что ты хотел бы, чтобы я прояснил, кстати? Какая-то часть осталась без ответа?
@TedLyngmo Спасибо, что спросили. Просто быстрое уточнение: когда поток ожидает блокировки и даже отправляется уведомление, но соответствующая блокировка еще не снята, поток не перейдет к следующей строке, верно? это сообщение не будет напечатано в приведенном выше примере, верно? он остается на этой строке, даже если время ожидания истекло? sharedRes.cv.wait_for(lk,3s);
@ Сэм Да. Из cppreference: «Атомарно освобождает lock, блокирует текущий исполняемый поток и добавляет его в список потоков, ожидающих *this. Поток будет разблокирован при выполнении notify_all() или notify_one() или по истечении относительного времени ожидания rel_time. Это также может быть разблокируется ложно. При разблокировке, независимо от причины, lock повторно захватывается и wait_for() закрывается." - поэтому при уведомлении он просто зависнет при попытке lock (или try_lock каким-то образом в фоновом режиме), что не удастся, пока поток, удерживающий блокировку, не освободит ее.
Понятно, что. Спасибо.
Спящий поток не будет работать до тех пор, пока он снова не получит блокировку.