Понятно ли, что произойдет, если поток, заблокированный на 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
каким-то образом в фоновом режиме), что не удастся, пока поток, удерживающий блокировку, не освободит ее.
Понятно, что. Спасибо.
Спящий поток не будет работать до тех пор, пока он снова не получит блокировку.