Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.52/25: Рейтинг темы: голосов - 25, средняя оценка - 4.52
901 / 478 / 93
Регистрация: 10.06.2014
Сообщений: 2,700

Условные переменные и ложные пробуждения

30.01.2018, 22:27. Показов 5000. Ответов 24
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Есть код
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
#include <iostream>
#include <thread>
#include <atomic>
#include <mutex>
#include <condition_variable>
 
std::atomic<int> flag = ATOMIC_VAR_INIT(0);;
std::mutex m;
std::condition_variable cv;
 
void dataProvider()
{
    while (1)
    {
        int n;
        std::cout << "Enter a variable\n";
        std::cin >> n;
        if (n > 0) {
            flag.store(n);
            cv.notify_one();
            break;
        }
    }
}
 
void dataChecker()
{
    std::unique_lock<std::mutex> lk(m);
    bool result = cv.wait_for(lk, std::chrono::minutes(1),[]() {
        std::cout << "Wake up\n";
        return flag.load() > 0;
    });
    if (result) {
        std::cout << "Data is: " << flag.load();
    }
}
 
int main()
{
    std::thread provider(dataProvider);
    std::thread checker(dataChecker);
    provider.join();
    checker.join();
}
В dataChecker используется условная переменная,
когда dataProvider вызовет notify_one, то функция потока dataCheker проснется и проверит условие.
Если условие ложно - поток пойдет спать дальше и так до тех пор пока коллбек не вернет true.

Но тут насколько я понимаю есть следующая проблема:
За несколько микросекунд до того как будут готовы данные, может случиться ложное пробуждение, и таким образом поток как я понимаю "проскочит" т.к данные будут готовы только через несколько микросекунд и т.к колбек вернет false, поток уснет и пойдет новый отсчет таймаута равный одной минуте. В итоге программа будет ждать еще одну лишнюю минуту...

Если такая проблема есть, то можно ли как нибудь избавиться от нее не отказываясь от использования условных переменных?
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
30.01.2018, 22:27
Ответы с готовыми решениями:

QWaitCondition и ложные пробуждения
Возникают ли ложные пробуждения при использовании QWaitCondition? Или при использовании QWaitCondition поток &quot;просыпается&quot;...

Условные переменные
Ещё раз здравствуйте! как следующий пример реализовать с помощью boost::condition cond; #include &lt;boost/thread.hpp&gt; #include...

Повторяю за Пузаном. Условные переменные (COND)
Написал сам код, который по команде pthread_cond_broadcast(&amp;cond); пробуждает не более одного потока! Пузан говорит, что...

24
184 / 192 / 48
Регистрация: 25.08.2011
Сообщений: 792
30.01.2018, 22:43
а зачем атомик переменную еще и кондишенал защищать?
0
901 / 478 / 93
Регистрация: 10.06.2014
Сообщений: 2,700
30.01.2018, 23:05  [ТС]
1Вирт1,
Атомик поставил чтоб барьер памяти был (хотя наверное уместнее было бы использовать atomic_thread_fence)
Иначе компилятор насколько я знаю мог бы переупорядочить код так, что бы сначала вызвался notify_one, а потом данные были бы сохранены в не атомик переменную.

До сохранения второй поток пробудился бы и в результате того что код был переупорядочен (возможно), данные были бы ещё не готовы.
0
192 / 128 / 52
Регистрация: 19.01.2010
Сообщений: 518
30.01.2018, 23:24
Цитата Сообщение от Undisputed Посмотреть сообщение
Иначе компилятор насколько я знаю мог бы переупорядочить код так, что бы сначала вызвался notify_one, а потом данные были бы сохранены в не атомик переменную.
Наскок я знаю, под х86 компиль не может менять порядок исполнения команд, а под другие платформы юзается мемори ордер для мютексов
0
901 / 478 / 93
Регистрация: 10.06.2014
Сообщений: 2,700
30.01.2018, 23:48  [ТС]
Selot,
Так у атомика вторым параметром и есть мемори ордер
Установлено значение по умолчанию поэтому явно передавать не стал

Добавлено через 1 минуту
Selot,
У интела х86 вроде как не переставляет код
Тоже такое слышал

Добавлено через 16 минут
Selot,
Кстати вы вроде сейчас говорите про барьеры компилятора

Реордеринг вроде как двухуровневый
1. На уровне компилятора
2. На уровне процессора

Если компилятор не увидит барьер памяти
То по идее независимо от типа процессора может переставить две строки местами
Потому что с его точки зрения логика не нарушится
Компилятор по идее рассматривает код как однопоточный

А вот процессор уже реордерить тот код (с переставленными строками) который ему дали не будет (если конечно же проц не выполняет реордеринг, как х86, если мы оба не ошибаемся)

Добавлено через 54 секунды
И получится в итоге не работающая программа
0
What a waste!
 Аватар для gray_fox
1610 / 1302 / 180
Регистрация: 21.04.2012
Сообщений: 2,733
31.01.2018, 06:48
Undisputed, надо вызывать notify в крит. секции, иначе запись во флаг + notify в продюсере могут "вклиниться" между проверкой флага в предикате и слипом в консьюмере.
0
901 / 478 / 93
Регистрация: 10.06.2014
Сообщений: 2,700
31.01.2018, 09:01  [ТС]
gray_fox,
Не очень понял что здесь не так
Там же только один поток который может вызвать нотифай.
Было бы их больше, тогда наверное крит. секция понадобилась

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

Он проснётся когда мы запишем данные и вызовем нотифай (ну или в случае ложного пробуждения)

Добавлено через 37 минут
gray_fox,
Вот тут есть пример для wait_for с колбеком, там так же у продюсера нет мьютекса
http://ru.cppreference.com/w/c... e/wait_for

А в версии wait_for без колбека мьютекс есть
http://ru.cppreference.com/w/c... n_variable

Во втором варианте с большой вероятностью мьютекс был использован только потому что бы сохранить порядок выполнения операций записи (наличие мьютекса неявно приводит к установки барьеров запрещающих реордеринг)
Иначе могло бы получиться так, что notified был бы равен true раньше чем данные попали бы в очередь
...
0
71 / 59 / 14
Регистрация: 20.12.2013
Сообщений: 720
31.01.2018, 09:01
Цитата Сообщение от Undisputed Посмотреть сообщение
поток уснет и пойдет новый отсчет таймаута равный одной минуте.
Я только прочитал эту тему в Страуструпе,так что может не понимаю, но почему пойдет новый отсчет? Таймаут вышел - все, из wait_for тоже вышли,и функция просто закончилась,разве нет?
p.s. и ложное пробуждение ведь тоже только для "plain wait(lck)"
0
901 / 478 / 93
Регистрация: 10.06.2014
Сообщений: 2,700
31.01.2018, 10:20  [ТС]
AndrSlav,
Нет, для wait_for так же есть вероятность ложного пробуждения
Посмотрите тут описание
http://ru.cppreference.com/w/c... e/wait_for

Дело в том что может случиться ложное пробуждение прямо перед тем как наступит таймаут, пока будет тратиться время на ложное пробуждение, часики нас ждать не будут, соответственно время отведённое на таймаут мы проскочим и поток уснёт снова, а следующее пробуждение уже будет по новому таймауту т.к прежний мы уже проскочили, и так может повторяться сколько угодно раз

Добавлено через 47 минут
AndrSlav,
Небольшое уточнение: каждое "просыпание", независимо от того было ли оно ложным, приводит к повторному сну потока с новым таймаутом. wait_for вернёт управление лишь тогда, когда предикат вернёт true.

The thread will be unblocked when notify_all() or notify_one() is executed, or when the relative timeout rel_time expires. It may also be unblocked spuriously. When unblocked, regardless of the reason, lock is reacquired and wait_for() exits. If this function exits via exception, lock is also reacquired. (until C++14)
В общем как я понял
Нужно юзать wait_until, там подобных проблем не возникает.
Правда может быть небольшая задержка ввиду тех же ложных пробуждений, но циклически это происходить не будет.
При желании можно организовать повторные вызовы wait_until, вызовы делать в цикле, но уже с изменённым сроком который можно вычислять после каждого пробуждения...
1
71 / 59 / 14
Регистрация: 20.12.2013
Сообщений: 720
31.01.2018, 10:27
Цитата Сообщение от Undisputed Посмотреть сообщение
Дело в том что может случиться ложное пробуждение прямо перед тем как наступит таймаут
Оказывается и с заданным временем может быть ложное пробуждение - не знал, но при использовании предиката по ссылке:
C++
1
2
3
4
5
std::unique_lock<std::mutex> lk(m);
    bool result = cv.wait_for(lk, std::chrono::minutes(1),[]() {
        std::cout << "Wake up\n";
        return flag.load() > 0;
    });
Equivalent to return wait_until(lock, std::chrono::steady_clock::now() + rel_time, std::move(pred));. This overload may be used to ignore spurious awakenings.
Т.е. с предикатом нет ложных срабатываний.

Добавлено через 5 минут
Цитата Сообщение от Undisputed Посмотреть сообщение
wait_for вернёт управление лишь тогда, когда предикат вернёт true
ИМХО там не то написано.
C++
1
2
3
4
5
6
7
8
9
int wmain()
{
 
    std::condition_variable cv;
    std::mutex mu;
    std::unique_lock<std::mutex> lk(mu);
    bool result = cv.wait_for(lk, std::chrono::seconds(10), []() {std::cout << "wait"<<'\n'; return 1 == 2; });
    lk.unlock();
}
Этот код не выполняется вечно у меня, только 2 раза wait выводит.
1
901 / 478 / 93
Регистрация: 10.06.2014
Сообщений: 2,700
31.01.2018, 10:44  [ТС]
Цитата Сообщение от AndrSlav Посмотреть сообщение
Этот код не выполняется вечно у меня, только 2 раза wait выводит.
Да, проверил у меня тоже самое. Но почему-то там написано что после вейкапа мьютекс будет захвачен повторно... Как предлагаете это понимать?

Цитата Сообщение от AndrSlav Посмотреть сообщение
Т.е. с предикатом нет ложных срабатываний.
Есть,
обратите внимание в вашем примере использовано wait_until, а у меня wait_for по тому принципу который я описал в сообщении выше (в добавлении)
It may also be unblocked spuriously. When unblocked, regardless of the reason, lock is reacquired and wait_for() exits. If this function exits via exception, lock is also reacquired. (until C++14)
2) Equivalent to return wait_until(lock, std::chrono::steady_clock::now() + rel_time, std::move(pred));. This overload may be used to ignore spurious awakenings.
0
71 / 59 / 14
Регистрация: 20.12.2013
Сообщений: 720
31.01.2018, 10:47
упс, не прав - пойду читать еще: вывелось 3 раза wait.

Добавлено через 1 минуту
хотя... или просто проверка несколько раз может пройти, но через 10 сек по любому будет выход - сейчас пока такую мысль думаю.

Добавлено через 1 минуту
Цитата Сообщение от Undisputed Посмотреть сообщение
This overload may be used to ignore spurious awakenings.
Кмк это все же к этой реализации wait_for относится (с предикатом).
0
901 / 478 / 93
Регистрация: 10.06.2014
Сообщений: 2,700
31.01.2018, 10:50  [ТС]
Цитата Сообщение от Undisputed Посмотреть сообщение
обратите внимание в вашем примере использовано wait_until, а у меня wait_for по тому принципу который я описал в сообщении выше (в добавлении)
точнее так:
обратите внимание в вашем примере использовано wait_until по тому принципу который я описал в сообщении выше (в добавлении), а у меня wait_for

извиняюсь за кривость сообщений
пишу торопясь
0
71 / 59 / 14
Регистрация: 20.12.2013
Сообщений: 720
31.01.2018, 10:51
Цитата Сообщение от Undisputed Посмотреть сообщение
после вейкапа мьютекс будет захвачен повторно... Как предлагаете это понимать?
при входе в wait мьютекс должен освобождаться - иначе все остальные потоки будут ждать бесконечно, если в них тоже он используется. А после выхода из wait снова захватывается, чтобы не разделять ресурсы.
0
901 / 478 / 93
Регистрация: 10.06.2014
Сообщений: 2,700
31.01.2018, 10:57  [ТС]
Цитата Сообщение от AndrSlav Посмотреть сообщение
Кмк это все же к этой реализации wait_for относится (с предикатом).
Там сначала пишется что возможны ложные просыпания
но если вы хотите избавиться от ложных просыпаний
можете использовать wait_until переназначая время ожидания

я сейчас должен покинуть форум,
если найдете доп. инфу буду благодарен если поделитесь

пока думаю так:
если уж использовать wait_for, то только в цикле
в предикате нужно возвращать true что бы понять были ли подготовлены данные которые мы ждем
далее если result будет false, значит данных нет и пробуждение ложное
и следующая итерация в цикле снова залочит wait_for на указанный таймаут
и так по кругу
0
71 / 59 / 14
Регистрация: 20.12.2013
Сообщений: 720
31.01.2018, 11:23
Может так?
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void dataChecker()
{
    while (1)
    {
        std::unique_lock<std::mutex> lk(m);
        bool result = cv.wait_for(lk, std::chrono::minutes(1), []() {
            std::cout << "Wake up\n";
            return flag2 > 0;
        });
        if (result) {
            std::cout << "Data is: " << flag2;
            break;
        }
 
    }
}
Добавлено через 6 минут
ps ну и обязательно
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void dataProvider()
{
    while (1)
    {
        int n;
        std::cout << "Enter a variable\n";
        std::cin >> n;
        if (n > 0) {
            flag.store(n);
            std::unique_lock<std::mutex> lk(m);
            cv.notify_one();
            break;
        }
    }
}
0
901 / 478 / 93
Регистрация: 10.06.2014
Сообщений: 2,700
31.01.2018, 12:04  [ТС]
AndrSlav,
Да именно так же думаю и я

Постараюсь вечером более подробно все расписать, все выводы которые у меня появились в этой теме
Ща с телефона не удобно писать много текста

Только не понял зачем вы поставили лок перед notify
Можете пошагово описать ситуацию при которой отсутствие лока перед нотифай может навредить?
0
71 / 59 / 14
Регистрация: 20.12.2013
Сообщений: 720
31.01.2018, 13:01
Цитата Сообщение от Undisputed Посмотреть сообщение
Только не понял зачем вы поставили лок перед notify
Я передумал)
0
What a waste!
 Аватар для gray_fox
1610 / 1302 / 180
Регистрация: 21.04.2012
Сообщений: 2,733
31.01.2018, 18:38
Цитата Сообщение от Undisputed Посмотреть сообщение
Не очень понял что здесь не так
Я представляю такую возможную последовательность выполнения:
1) dataChecker засыпает в wait_for, просыпается (ложное пробуждение) и захватывает мютекс m, проверяет условие flag > 0.
2) dataProvider сохраняет значение во flag и вызывает notify. dataChecker этот сигнал не получит, ведь он ещё не спит.
3) dataChecker освобождает мютекс и засыпает на оставшееся по таймеру время.
2
71 / 59 / 14
Регистрация: 20.12.2013
Сообщений: 720
31.01.2018, 19:17
Да, все же нужен лок)
В дополнение к gray_fox
http://en.cppreference.com/w/c... notify_one
The effects of notify_one()/notify_all() and each of the three atomic parts of wait()/wait_for()/wait_until() (unlock+wait, wakeup, and lock) take place in a single total order that can be viewed as modification order of an atomic variable: the order is specific to this individual condition_variable. This makes it impossible for notify_one() to, for example, be delayed and unblock a thread that started waiting just after the call to notify_one() was made.
А по поводу почему выводятся значения в предикате - на самом деле код для wait_for не пропускает случайные просыпания, но предикат выполняется:
https://msdn.microsoft.com/ru-... 74724.aspx
C++
1
2
3
4
while(!Pred())
   if(wait_for(Lck, Rel_time) == cv_status::timeout)
      return Pred();
return true;
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
31.01.2018, 19:17
Помогаю со студенческими работами здесь

Сменить кнопку пробуждения
Здравствуйте.... Помогите пожалуйста сменить кнопку пробуждения моего ПК. Я незнаю как именно это можно сделать, но видел что такое...

Синий экран после пробуждения
Здраствуйте, друзья. Замучала проблема, когда комп после спящего режима включаешь - он сразу включается как с нуля, при этом на входе в...

Ноутбук зависает после пробуждения
Здравствуйте, есть проблема: После пробуждения из режима сна ноутбук постоянно зависает. С чем это может быть связано? Х-ки ноута...

Логи пробуждения/включения Windows 10
Подскажите, пожалуйста, где можно посмотреть время пробуждения/перехода в спящий режим (включения/выключения) Windows 10 в удобной форме,...

Ложные пользователи форума
Здравствуйте. Заметил сомнительных пользователей на форуме. Регулярно регистрируются, ставят в поле сайт разные адреса, которые ведут на...


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

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
PhpStorm 2025.3: WSL Terminal всегда стартует в ~
and_y87 14.12.2025
PhpStorm 2025. 3: WSL Terminal всегда стартует в ~ (home), игнорируя директорию проекта Симптом: После обновления до PhpStorm 2025. 3 встроенный терминал WSL открывается в домашней директории. . .
Access
VikBal 11.12.2025
Помогите пожалуйста !! Как объединить 2 одинаковые БД Access с разными данными.
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
Создание Single Page Application на фреймах
krapotkin 16.11.2025
Статья исключительно для начинающих. Подходы оригинальностью не блещут. В век Веб все очень привыкли к дизайну Single-Page-Application . Быстренько разберем подход "на фреймах". Мы делаем одну. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru