Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[18.221.208.183] |
|
Страницы: (4) 1 [2] 3 4 все ( Перейти к последнему сообщению ) |
Сообщ.
#16
,
|
|
|
Цитата JoeUser @ На условной переменной ты можешь собрать произвольной сложности условие. Получить аналог WaitForMultipleObjects() несложно. Я загуглил, на SO ответили однозначно, что нужен велосипед (You can't, and must redesign), т.к. conditial variable только одно состояние отслеживает. |
Сообщ.
#17
,
|
|
|
К слову, зачем тебе там нужен WaitForMultiplyObjects ? Почему сам цикл не переделать как то так?
while(!BreakFlag) { std::unique_lock<std::mutex> lk(cv_m); if(cv.wait_for(lk, std::chrono::milliseconds(100000), [](){return ReadyFlag;})) { //! ReadyFlag is true doSomething(); } else { //! timeout } } Добавлено Цитата JoeUser @ А программа всего лишь что и делает в фоне - только мониторит состояние фискального аппарата (если я не ошибаюсь по COM-порту). К слову: Монитор Очень познавательная статья. Добавлено Ну или еще для любителей велосипедов решение на семафорах, то что судя по всему предлагал ЫукпШ: https://habr.com/ru/post/261273/ |
Сообщ.
#18
,
|
|
|
Цитата shm @ Вопрос странный. Поток может быть в 2 состояниях: активным и в спячке. В спячке поток ожидает событий для пробуждения. События могут быть сгенерированы в пользовательском коде (в других потоках) или ОС как ответ на ожидание каких-то аппаратных событий, например, тех же данных на COM порту. Ну попробую еще раз объяснить ... Как-то недавно Киля мне порекомендовал либу Interception. Процесс чтения данных там можно осуществлять двумя функциями (вернее ожидание данных на устройстве): 1) interception_wait 2) interception_wait_with_timeout Так вот первую функцию я не могу использовать по одной простой причине - пока она бесконечно висит я не могу завершить поток нормальным способом если вдруг мне это надо. Поэтому я использую вторую функцию с указанием тайм аута. Нет данных - пошла очередная итерация цикла. А вот в ней я уже могу анализировать BreakFlag. Если увижу что взведен - выхожу из цикла и завершаю поток. Советовали и мьютексы, и семафоры, и cоndition_variable. Но куда тут их всунуть? Кто извне (из другого треда) будет их забирать-отпускать? А вот теперь, допустим, другая похожая задача. Я должен осуществлять другую операцию в потоке - вместо interception_wait_with_timeout ... что нибудь типа std::filesystem::exists("helloworld.txt"). И если файл найден - удалять. Делать пока поток не будет прерван. Если sleep в цикл не поставлю - "комп через час-два закипит"? |
Сообщ.
#19
,
|
|
|
JoeUser, давай пройдёмся по теории. Её тут уже коснулись, но систематизировать не помешает.
Итак, существует два варианта реакции на изменившиеся окружающие условия: либо тебе об этом кто-то сообщает, и тогда нет проблем, вешаешься на сигнал и ждёшь; либо никаких нотификаций не предусмотрено, тогда у тебя остаётся вариант пуллить это событие самостоятельно. Второй вариант, вообще говоря, сводится к первому, надо лишь самому реализовать эту нотифю, вопрос лишь в том, стоит ли. Но как бы там ни было, в первом случае задача решена до тебя, и этот вариант уже рассматривался выше, но по ходу это не тот вариант, который тебя интересует, так что второй. Любой пуллинг основан на периодическом опросе. Вопрос о его реализации, думаю, стоит не в том ключе, мол, как это сделать, а в том, мол, как это сделать эффективно. Wound тебе предложил условные переменные, что даёт половину решения, основанное на нотификациях, условия которых ты сам же и определяешь. Не самое удобное, возможно, зато универсальное: написал один раз и забыл. Вторая половина упирается в то, что нельзя ничего сказать о величине периода опроса, т.к. это сильно зависит от характера процесса, чьё состояние опрашивается. И от него же зависит формулировка условия для нотификации. К примеру, опрос своего файлика ты можешь оформить просто как std::condition_variable cv; std::mutex cv_m; using namespace std::chrono_literals; /* ... */ std::unique_lock<std::mutex> lk(cv_m); cv.wait_for(lk, 1s, []{ return std::filesystem::exists("helloworld.txt"); }); Проблема же всплывает, если тебе нужно отслеживать несколько нотификаций сразу и по-разному на них реагировать. Ну так выше я уже писал, что условие ты можешь поставить любое. Напиши что-то типа std::atomic<bool> break_event = false; /* ... */ cv.wait_for(lk, 1s, []{ return std::filesystem::exists("helloworld.txt") || break_event; }); P.S. В принципе при таком подходе уже и таймаут не нужен. |
Сообщ.
#20
,
|
|
|
Цитата JoeUser @ Советовали и мьютексы, и семафоры, и cоndition_variable. Но куда тут их всунуть? Кто извне (из другого треда) будет их забирать-отпускать? А ранее ты сам же на этот вопрос и отвечаешь : Цитата JoeUser @ Так вот первую функцию я не могу использовать по одной простой причине - пока она бесконечно висит я не могу завершить поток нормальным способом если вдруг мне это надо. Поэтому я использую вторую функцию с указанием тайм аута. Нет данных - пошла очередная итерация цикла. А вот в ней я уже могу анализировать BreakFlag. Если увижу что взведен - выхожу из цикла и завершаю поток. На сколько я понял у тебя это как раз взаимосвязано. Там где тебе нужно поменять флаг - там ты пишешь notify_one/notify_all, там где у тебя флаги проверяются - ты пишешь wait/wait_for/wait_until/etc. У тебя по условию задачи - есть сигнал и есть его обработчик. Сейчас ты это все разруливаешь флагами, у тебя в каком то месте флагу присваивается значение true, а в другом месте этот самый флаг на значение true проверяется. Тебе в том месте - где присваивается значение флагу true - нужно изменить условие(поменять значение на true своему флагу например) и вызвать notify, а в том месте - где у тебя флаг проверяется тебе нужно заюзать wait_for. Что тут непонятного то? |
Сообщ.
#21
,
|
|
|
Qraizer, да, именно о пуллинге идет речь. потому как не от каждой операционной системы и не по каждому состоянию получишь какое либо сообщение.
Вот я накидал пример того, о чем спрашиваю: #if __cplusplus < 201703L #error "C++17 podavai suda!" #endif #include <filesystem> #include <iostream> #include <string> #include <thread> #include <atomic> using namespace std::chrono_literals; std::atomic<bool> BreakFlag = ATOMIC_FLAG_INIT; std::atomic<bool> TerminateFlag = ATOMIC_FLAG_INIT; std::string FileName = "C:/Temp/helloworld.txt"; void ThreadFunc() { do { if (BreakFlag) break; try { if (std::filesystem::exists(FileName)) std::filesystem::remove(FileName); } catch (...) {} // std::this_thread::sleep_for(1ms); // Если не раскоментировать - будет жесть, 17-20% жрет CPU!!! } while (true); TerminateFlag = true; } int main() { std::string str; std::thread Thread(ThreadFunc); Thread.detach(); std::cout << "Type \"Exit\" to exit..." << std::endl; do { std::cin >> str; } while (str != "Exit"); BreakFlag = true; while (!TerminateFlag) std::this_thread::yield(); std::cout << "Done." << std::endl; return 0; } Прога элементарная. Ждет написания в консоли слова Exit и нажатие [Enter]. А пока ждет - работает. А именно ищет файл "C://Temp//helloworld.txt" и если находит - грохает его. Перепишите мне с мьютексами, семафорами, или condition_variable чтобы стало лучше/правильнее. И что именно станет правильнее и лучше? ЗЫ: Если раскоментить std::this_thread::sleep_for(1ms) - загрузка CPU у меня 0% |
Сообщ.
#22
,
|
|
|
Вот например есть какой то синтетический пример, показывающий как это все работает: https://ru.cppreference.com/w/cpp/thread/co...riable/wait_for
#include <iostream> #include <atomic> #include <condition_variable> #include <thread> #include <chrono> std::condition_variable cv; std::mutex cv_m; std::atomic<int> i = ATOMIC_VAR_INIT(0); void waits(int idx) { std::unique_lock<std::mutex> lk(cv_m); if(cv.wait_for(lk, std::chrono::milliseconds(idx*100), [](){return i == 1;})) std::cerr << "Thread " << idx << " finished waiting. i == " << i << '\n'; else std::cerr << "Thread " << idx << " timed out. i == " << i << '\n'; } void signals() { std::this_thread::sleep_for(std::chrono::milliseconds(120)); std::cerr << "Notifying...\n"; cv.notify_all(); std::this_thread::sleep_for(std::chrono::milliseconds(100)); i = 1; std::cerr << "Notifying again...\n"; cv.notify_all(); } int main() { std::thread t1(waits, 1), t2(waits, 2), t3(waits, 3), t4(signals); t1.join(); t2.join(), t3.join(), t4.join(); } |
Сообщ.
#23
,
|
|
|
Цитата Wound @ На сколько я понял у тебя это как раз взаимосвязано. Там где тебе нужно поменять флаг - там ты пишешь notify_one/notify_all, там где у тебя флаги проверяются - ты пишешь wait/wait_for/wait_until/etc. Киля, вон я выше накидал тестовую прогу. К тебе тож просьба - перепиши ее с твоими wait/async/future, ну как ты советовал. Добавлено Цитата Wound @ Вот например есть какой то синтетический пример Мой перепиши, плс, то что мне надо. |
Сообщ.
#24
,
|
|
|
Цитата JoeUser @ Прога элементарная. Ждет написания в консоли слова Exit и нажатие [Enter]. А пока ждет - работает. А именно ищет файл "C://Temp//helloworld.txt" и если находит - грохает его. Перепишите мне с мьютексами, семафорами, или condition_variable чтобы стало лучше/правильнее. И что именно станет правильнее и лучше? А причем тут эта прога и то что ты хочешь? Ты пишешь - как мне усыпить поток, чтоб он мне CPU не жрал. И просишь переписать твой пример - где у тебя усыплением потока и не пахнет, где один поток файлы ищет, а второй ждет пока ему Exit не напишут. Как ты свой пример перепишешь с WaitForMultiplyObjects ? Я не понимаю твоего примера. Добавлено Цитата JoeUser @ Мой перепиши, плс, то что мне надо. Не могу. У тебя приведенный пример, противоречит тому, что ты тут две страницы пояснял. Я не знаю что тебе тут переписать? В примере приведеном тобою главный поток ждет, пока ему не напишут Exit, а потом тупо выходит. Или тебе нужно застопорить поток там, где у тебя std::this_thread::yield(); ? Тогда как то так будет: #include <filesystem> #include <iostream> #include <string> #include <thread> #include <atomic> #include <condition_variable> using namespace std::chrono_literals; bool BreakFlag = ATOMIC_FLAG_INIT; bool TerminateFlag = ATOMIC_FLAG_INIT; std::string FileName = "C://Temp//helloworld.txt"; std::condition_variable cv; std::mutex cv_m; void ThreadFunc() { do { if (BreakFlag) { cv.notify_one(); break; } try { if (std::filesystem::exists(FileName)) std::filesystem::remove(FileName); } catch (...) {} // std::this_thread::sleep_for(1ms); // Если не раскоментировать - будет жесть, 17-20% жрет CPU!!! } while (true); TerminateFlag = true; } int main() { std::string str; std::thread Thread(ThreadFunc); Thread.detach(); std::cout << "Type \"Exit\" to exit..." << std::endl; do { std::cin >> str; } while (str != "Exit"); BreakFlag = true; while (true) { std::unique_lock<std::mutex> lk(cv_m); if (cv.wait_for(lk, std::chrono::seconds(1), [=]() {return TerminateFlag; })) { break; } } std::cout << "Done." << std::endl; return 0; } |
Сообщ.
#25
,
|
|
|
Цитата Wound @ Ты пишешь - как мне усыпить поток Да ё-мое! Ну почитай ты название топика! Я спрашивал что нужно чтобы прога не жрала CPU без надобности и только. А то что я усыпляю его на доли секунды - это как одно из решений. Я не спрашиваю как его полностью усыпить! Добавлено Цитата Wound @ Я не понимаю твоего примера. Я старался! |
Сообщ.
#26
,
|
|
|
Цитата JoeUser @ Да ё-мое! Ну почитай ты название топика! Я спрашивал что нужно чтобы прога не жрала CPU без надобности и только. А то что я усыпляю его на доли секунды - это как одно из решений. Я не спрашиваю как его полностью усыпить! В каком месте у тебя прога жрет CPU без надобности? Ты в первом посту пишешь: Цитата JoeUser @ В одной из программ, которую подогнали разработчики, постоянная загрузка CPU висит в районе 25%. И это на Core i3 2.5GHz. А программа всего лишь что и делает в фоне - только мониторит состояние фискального аппарата (если я не ошибаюсь по COM-порту). Все мои попытки убедить разработчиков, что это нонсенс, упираются в "стену". Отвечают что-то в роде "нам важно, чтобы система с фискальным аппаратом работала без задержек, мы лучше знаем" Я подозреваю, что залепили в своем коде что-то в виде: Где в твоем примере мониторинг состояния, любого? |
Сообщ.
#27
,
|
|
|
Цитата Wound @ Тогда как то так будет: И улучшений - нуль! Если слип-задержку не раскоментить - как жрала прога 17% CPU, так и жрет. Это сразу было видно по твоему коду. Но я проверил на всяк случай: |
Сообщ.
#28
,
|
|
|
Цитата JoeUser @ А именно ищет файл "C://Temp//helloworld.txt" и если находит - грохает его. Перепишите мне с мьютексами, семафорами, или condition_variable чтобы стало лучше/правильнее. И что именно станет правильнее и лучше? Эта задача правильнее и лучше не решается. Ты не можешь подписаться в системе на такое событие - "наличие такого-то файла". А вот следить за изменениями в указанной директории вроде можно. (я этого не делал, поэтому определённо сказать не могу). тут Поэтому заснуть потоком, но ждать событие "изменения в папке" - можно. --- И потом - что значит "правильнее и лучше" ? Это всё субъективно. Для того, что ты показал, сделанное достаточно. Но если бы ты задавал имя файла для поиска и уничтожения в окошке эдита, а потом жал на кнопку, тогда надо: 1. Заснуть потоком на Wait. 2. Ждём 2 события - Exit и Work. 3. Если завершаем приложение, взводим Exit 4. Если жмём на кнопку "Уничтожить указанный файл" - значит взводим Work. Дальше начинается поиск файла в папке и всех вложенных папках и уничтожение его. Потом снова засыпаем. |
Сообщ.
#29
,
|
|
|
Цитата Wound @ Где в твоем примере мониторинг состояния, любого? Вот тут: if (std::filesystem::exists(FileName)) |
Сообщ.
#30
,
|
|
|
Цитата ЫукпШ @ Ты не можешь подписаться в системе на такое событие - "наличие такого-то файла". Ну в разных ОС по разному, в Линухе, к примеру, это можно решить с помощью inotify. Там много чего можно отслеживать, в том числе и появление файла, и его закрытие после записи. |