Форум программистов, компьютерный форум, киберфорум
C++
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.91/34: Рейтинг темы: голосов - 34, средняя оценка - 4.91
49 / 2 / 0
Регистрация: 26.09.2014
Сообщений: 98

Не понятно как работает пример std::condition_variable::wait

02.03.2020, 13:05. Показов 7424. Ответов 13
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Здравствуйте! Разбираюсь с std::condition_variable и не могу понять пример кода из cppreference.com. Вот он:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <iostream>
#include <condition_variable>
#include <thread>
#include <chrono>
 
std::condition_variable cv;
std::mutex cv_m; // This mutex is used for three purposes:
                 // 1) to synchronize accesses to i
                 // 2) to synchronize accesses to std::cerr
                 // 3) for the condition variable cv
int i = 0;
 
void waits()
{
    std::unique_lock<std::mutex> lk(cv_m);
    std::cerr << "Waiting... \n";
    cv.wait(lk, [] {return i == 1; });
    std::cerr << "...finished waiting. i == 1\n";
}
 
void signals()
{
    std::this_thread::sleep_for(std::chrono::seconds(1));
    {
        std::lock_guard<std::mutex> lk(cv_m);
        std::cerr << "Notifying...\n";
    }
    cv.notify_all();
 
    std::this_thread::sleep_for(std::chrono::seconds(1));
 
    {
        std::lock_guard<std::mutex> lk(cv_m);
        i = 1;
        std::cerr << "Notifying again...\n";
    }
    cv.notify_all();
}
 
int main()
{
    std::thread t1(waits), t2(waits), t3(waits), t4(signals);
    t1.join();
    t2.join();
    t3.join();
    t4.join();
}
Вопрос:
В "waits()" мьютекс блокируется и поток останавливается, ожидая "истинного" оповещения. "signals()" параллельно пытается заблокировать тот же мьютекс, но он уже принадлежит самому первому "waits", который ждет от "signals" оповещения, а "signals" он встанет, ожидая освобождения мьютекса. По мне, получается что-то вроде дедлока. Но этот код априори правильный, так что я упускаю?

Дополнительный вопрос:
Как дождаться остановки цикла, работающего в параллельном потоке, если было вызвано thread::detach()? Собственно из-за этого я и изучаю std::condition_variable. На данный момент я имею это:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class MyClass
{
    void loop();
    void breakLoop();
 
    std::atomic_bool m_loopActive = true;
    std::atomic_bool m_loopStopped = false;
    int i = 0;
public:
    MyClass();
    ~MyClass();
 
 
};
 
MyClass::MyClass()
{
    std::thread th(&MyClass::loop, this);
    th.detach();
}
 
MyClass::~MyClass()
{
    breakLoop();
}
 
void MyClass::loop()
{
    while (m_loopActive)
    {
        ++i;
        std::this_thread::sleep_for(std::chrono::milliseconds(1));
    }
    m_loopStopped = true;
}
 
void MyClass::breakLoop()
{
    m_loopActive = false;
    while (!m_loopStopped) {};
}
В цикле используются члены класса. При удалении класса, цикл все еще может работать и попытаться получить доступ к несуществующим членам. Поэтому нужно дождаться его завершения в деструкторе. То что я делаю сейчас, мне кажется неправильным. Будет ли использование здесь std::condition_variable уместным? Или оставить так как было? А может есть решение лучше?
0
Лучшие ответы (1)
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
02.03.2020, 13:05
Ответы с готовыми решениями:

C++11, потоки, std::condition_variable
Проблема в том, что в коде ниже сначала работает лишь поток th1, а затем только th2 (поток th1 бездействует). Хотелось бы, чтобы при...

Метод работает как то не понятно
template &lt;typename TBase&gt; bool TArray &lt;TBase&gt;:: Addition (TBase s ) { ...

Не совсем понятно как работает такой код
Есть консольное приложение, которое при работе выводит данные. Непонятно как работает такой код: for (;;) { char option; ...

13
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
02.03.2020, 13:22
Цитата Сообщение от Maks53 Посмотреть сообщение
В "waits()" мьютекс блокируется и поток останавливается, ожидая "истинного" оповещения. "signals()" параллельно пытается заблокировать тот же мьютекс, но он уже принадлежит самому первому "waits", который ждет от "signals" оповещения, а "signals" он встанет, ожидая освобождения мьютекса. По мне, получается что-то вроде дедлока. Но этот код априори правильный, так что я упускаю?
std::condition_variable::wait(....) разблокирует мьютекс, ждёт notify_all/one, потом снова блокирует.
1
Неэпический
 Аватар для Croessmah
18149 / 10731 / 2067
Регистрация: 27.09.2012
Сообщений: 27,035
Записей в блоге: 1
02.03.2020, 13:24
Цитата Сообщение от Maks53 Посмотреть сообщение
Но этот код априори правильный, так что я упускаю?
То, что wait разблокирует мьютекс?
1
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
02.03.2020, 13:29
Лучший ответ Сообщение было отмечено Maks53 как решение

Решение

Цитата Сообщение от Maks53 Посмотреть сообщение
Как дождаться остановки цикла, работающего в параллельном потоке, если было вызвано thread::detach()? Собственно из-за этого я и изучаю std::condition_variable. На данный момент я имею это:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class MyClass
{
    void loop();
    void breakLoop();
 
    std::mutex m_mx;
    std::condition_variable m_cv;
 
    volatile bool m_loopActive = true;
    volatile bool m_loopStopped = false;
    int i = 0;
public:
    MyClass();
    ~MyClass();
 
 
};
 
MyClass::MyClass()
{
    std::thread th(&MyClass::loop, this);
    th.detach();
}
 
MyClass::~MyClass()
{
    breakLoop();
}
 
void MyClass::loop()
{
    std::unique_lock lock(m_mx);
    while (m_loopActive)
    {
        ++i;
        m_cv.wait_for(std::chrono::milliseconds(1));
    }
    m_loopStopped = true;
    m_cb.notify_all();
}
 
void MyClass::breakLoop()
{
    std::unique_lock lock(m_mx);
    m_loopActive = false;
    while (!m_loopStopped)
        m_cv.wait();
}
1
49 / 2 / 0
Регистрация: 26.09.2014
Сообщений: 98
02.03.2020, 14:13  [ТС]
Цитата Сообщение от Croessmah Посмотреть сообщение
wait разблокирует мьютекс
Потом же снова заблокирует, как написал господин oleg-m1973? тогда получается что поток с waits() почти постоянно удерживает мьютекс закрытым?
0
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
02.03.2020, 14:31
Цитата Сообщение от Maks53 Посмотреть сообщение
Потом же снова заблокирует, как написал господин oleg-m1973? тогда получается что поток с waits() почти постоянно удерживает мьютекс закрытым?
Нет, большую часть времени он спит внутри std::condition_variable::wait(), т.е. держит мьютекс разблокированным.
Кстати, меня там в примере ошибки:
m_cv.wait_for(lock, std::chrono::milliseconds(1));
m_cv.wait(lock);
1
49 / 2 / 0
Регистрация: 26.09.2014
Сообщений: 98
02.03.2020, 14:37  [ТС]
oleg-m1973, Спасибо.
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
m_cv.wait_for(lock, std::chrono::milliseconds(1));
А почему так вместо std::this_thread::sleep_for?
0
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
02.03.2020, 14:54
Цитата Сообщение от Maks53 Посмотреть сообщение
А почему так вместо std::this_thread::sleep_for?
В данном случае - чтоб разблокировать мьютекс на время ожидания.
А вообще - лучше так вообще не усыплять потоки, работать по-событиям.
Это здесь у тебя задержка одна миллисекунда, а поставишь например 10 сек (почему бы и нет?), он 10 секунд и будет висеть, прежде, чем остановится
1
49 / 2 / 0
Регистрация: 26.09.2014
Сообщений: 98
02.03.2020, 15:03  [ТС]
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
А вообще - лучше так вообще не усыплять потоки, работать по-событиям.
Это как? Что-то вроде паттерна "Подписчик"?

А будет ли лучше, если конкретно в моем случае использовать std::notify_all_at_thread_exit? как это будет выглядеть?
0
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
02.03.2020, 15:10
Цитата Сообщение от Maks53 Посмотреть сообщение
А будет ли лучше, если конкретно в моем случае использовать std::notify_all_at_thread_exit? как это будет выглядеть?
В данном случае - плохо. У тебя поток останавливается в деструкторе и condition_variable c мьютексом будут уничтожены раньше, чем поток вызовет этот notify_all.
0
49 / 2 / 0
Регистрация: 26.09.2014
Сообщений: 98
02.03.2020, 15:14  [ТС]
oleg-m1973, А разве члены класса не уничтожаются только после выполнения тела деструктора?
0
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
02.03.2020, 15:17
Цитата Сообщение от Maks53 Посмотреть сообщение
oleg-m1973, А разве члены класса не уничтожаются только после выполнения тела деструктора?
Именно. Только твой поток не член класса, он сам по-себе работает и обратится к этим членам, когда они будут уничтожены.
0
49 / 2 / 0
Регистрация: 26.09.2014
Сообщений: 98
02.03.2020, 15:22  [ТС]
oleg-m1973, Понял, большое спасибо!
0
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
02.03.2020, 15:27
Цитата Сообщение от Maks53 Посмотреть сообщение
oleg-m1973, Понял, большое спасибо!
Ну и до кучи - у таких классов, которые запускают потоки в конструкторе или останавливают в деструкторе, не стоит делать виртуальных методов, которые могут быть вызваны в потоке. А ещё лучше, вообще помечать их final, чтоб и желания наследоваться не возникало
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
02.03.2020, 15:27
Помогаю со студенческими работами здесь

как i двигается понятно но вот не понятно как это делает j ?
Здравствуйте, вопрос очень глупы но все же есть цикл for (int i = 0,j = 0; i &lt; source.length; i++) как i двигается понятно но вот не...

Не работает std::cout || std::cin
#include &quot;Account.h&quot; #include &lt;string&gt; #include &lt;iostream&gt; using std::cout; Account :: Account(int startBalance) { ...

Кто-нибудь может понятно объяснить как работает линейный конгруэнтный метод создания случайный чисел ?
Всю информацию которую находил, объяснялась поверхностно

Как работает std::declval?
В общем понятно - позволяет использовать в decltype тип который возвращает метод шаблонного аргумента даже если у него нет конструктора по...

Как работает std::deque?
Пытаюсь разобраться в работе std-шного дека. Веб-серфинг дал следующее: Данные хранятся в куче небольшими блоками(массивами) в виде...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
14
Ответ Создать тему
Новые блоги и статьи
Уведомление о неверно выбранном значении справочника
Maks 06.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа "НарядПутевка", разработанного в конфигурации КА2. Задача: уведомлять пользователя, если в документе выбран неверный склад. . .
Установка Qt Creator для C и C++: ставим среду, CMake и MinGW без фреймворка Qt
8Observer8 05.04.2026
Среду разработки Qt Creator можно установить без фреймворка Qt. Есть отдельный репозиторий для этой среды: https:/ / github. com/ qt-creator/ qt-creator, где можно скачать установщик, на вкладке Releases:. . .
AkelPad-скрипты, структуры, и немного лирики..
testuser2 05.04.2026
Такая программа, как AkelPad существует уже давно, и также давно существуют скрипты под нее. Тем не менее, прога живет, периодически что-то не спеша дополняется, улучшается. Что меня в первую очередь. . .
Отображение реквизитов в документе по условию и контроль их заполнения
Maks 04.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа "ПланированиеСпецтехники", разработанного в конфигурации КА2. Данный документ берёт данные из другого нетипового документа. . .
Фото всей Земли с борта корабля Orion миссии Artemis II
kumehtar 04.04.2026
Это первое подобное фото сделанное человеком за 50 лет. Снимок называют новым вариантом легендарной фотографии «The Blue Marble» 1972 года, сделанной с борта корабля «Аполлон-17». Новое фото. . .
Вывод диалогового окна перед закрытием, если документ не проведён
Maks 04.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа "СписаниеМатериалов", разработанного в конфигурации КА2. Задача: реализовать программный контроль на предмет проведения документа. . .
Программный контроль заполнения реквизитов табличной части документа
Maks 02.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа "СписаниеМатериалов", разработанного в конфигурации КА2. Задача: 1. Реализовать контроль заполнения реквизита. . .
wmic не является внутренней или внешней командой
Maks 02.04.2026
Решение: DISM / Online / Add-Capability / CapabilityName:WMIC~~~~ Отсюда: https:/ / winitpro. ru/ index. php/ 2025/ 02/ 14/ komanda-wmic-ne-naydena/
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru