Если поток, заблокированный в std::condition_variable, уведомлен, но блокировка связанного с ним мьютекса не снята, каков будет результат?

Понятно ли, что произойдет, если поток, заблокированный на 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();
}

Спящий поток не будет работать до тех пор, пока он снова не получит блокировку.

tkausl 13.02.2023 18:19
Laravel с Turbo JS
Laravel с Turbo JS
Turbo - это библиотека JavaScript для упрощения создания быстрых и высокоинтерактивных веб-приложений. Она работает с помощью техники под названием...
Типы ввода HTML: Лучшие практики и советы
Типы ввода HTML: Лучшие практики и советы
HTML, или HyperText Markup Language , является стандартным языком разметки, используемым для создания веб-страниц. Типы ввода HTML - это различные...
Аутсорсинг разработки PHP для индивидуальных веб-решений
Аутсорсинг разработки PHP для индивидуальных веб-решений
Услуги PHP-разработки могут быть экономически эффективным решением для компаний, которые ищут высококачественные услуги веб-разработки по доступным...
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
Слишком много useState? Давайте useReducer!
Слишком много useState? Давайте useReducer!
Современный фронтенд похож на старую добрую веб-разработку, но с одной загвоздкой: страница в браузере так же сложна, как и бэкенд.
Узнайте, как использовать теги &lt;ul&gt; и &lt;li&gt; для создания неупорядоченных списков в HTML
Узнайте, как использовать теги <ul> и <li> для создания неупорядоченных списков в HTML
HTML предоставляет множество тегов для структурирования и организации содержимого веб-страницы. Одним из наиболее часто используемых тегов для...
1
1
67
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий
Если поток, который заблокирован в std::condition_variable, уведомлен, но блокировка связанного с ним mutex не снята, что произойдет?

Он будет продолжать wait / wait_for до тех пор, пока не сможет снова получить блокировку.

Когда std::condition_variable::wait и wait_for возвращаются (по какой-либо причине), блокировка снова удерживается, поэтому вам не нужно об этом беспокоиться.

Он может даже вернуться из wait без каких-либо уведомлений (ложных пробуждений) - но, несмотря ни на что, блокировка будет восстановлена, когда вызов вернется.

Если я правильно помню, condition_variables могут ложно просыпаться, поэтому всегда используйте wait/wait_for с предикатом. Таким образом, условная переменная возобновит ожидание, если условие все еще не выполнено.

Pepijn Kramer 13.02.2023 18:37

@PepijnKramer Да, ложные пробуждения реальны (как я уже упоминал в ответе), но даже если это причина возврата wait, блокировка удерживается, когда она возвращается.

Ted Lyngmo 13.02.2023 18:38

Спасибо за ответ. Не могли бы вы уточнить это подробнее. Меня немного смущает следующее утверждение? что вы подразумеваете под «возвратом» в этом контексте? "Когда std::condition_variable::wait и wait_for возвращаются (по какой-либо причине), блокировка снова удерживается, так что вам не нужно об этом беспокоиться." " ... но, несмотря ни на что, блокировка повторно получена когда звонок вернется».

Sam 13.02.2023 19:47

@Sam Это просто означает, что когда вы вызываете cv.wait или cv.wait_for, он «зависает», а когда он возвращается из зависания, блокировка снова получена.

Ted Lyngmo 13.02.2023 19:49

@Sam Есть что-нибудь еще, что ты хотел бы, чтобы я прояснил, кстати? Какая-то часть осталась без ответа?

Ted Lyngmo 13.02.2023 20:19

@TedLyngmo Спасибо, что спросили. Просто быстрое уточнение: когда поток ожидает блокировки и даже отправляется уведомление, но соответствующая блокировка еще не снята, поток не перейдет к следующей строке, верно? это сообщение не будет напечатано в приведенном выше примере, верно? он остается на этой строке, даже если время ожидания истекло? sharedRes.cv.wait_for(lk,3s);

Sam 13.02.2023 21:02

@ Сэм Да. Из cppreference: «Атомарно освобождает lock, блокирует текущий исполняемый поток и добавляет его в список потоков, ожидающих *this. Поток будет разблокирован при выполнении notify_all() или notify_one() или по истечении относительного времени ожидания rel_time. Это также может быть разблокируется ложно. При разблокировке, независимо от причины, lock повторно захватывается и wait_for() закрывается." - поэтому при уведомлении он просто зависнет при попытке lock (или try_lock каким-то образом в фоновом режиме), что не удастся, пока поток, удерживающий блокировку, не освободит ее.

Ted Lyngmo 13.02.2023 21:06

Понятно, что. Спасибо.

Sam 13.02.2023 23:05

Другие вопросы по теме