На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: Qraizer, Hsilgos
Страницы: (4) 1 [2] 3 4  все  ( Перейти к последнему сообщению )  
> Разгрузка CPU в потоке
    Цитата JoeUser @
    Я загуглил, на SO ответили однозначно, что нужен велосипед (You can't, and must redesign), т.к. conditial variable только одно состояние отслеживает.
    На условной переменной ты можешь собрать произвольной сложности условие. Получить аналог WaitForMultipleObjects() несложно.
      К слову, зачем тебе там нужен WaitForMultiplyObjects ? Почему сам цикл не переделать как то так?
      ExpandedWrap disabled
        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/
        Цитата shm @
        Вопрос странный. Поток может быть в 2 состояниях: активным и в спячке. В спячке поток ожидает событий для пробуждения. События могут быть сгенерированы в пользовательском коде (в других потоках) или ОС как ответ на ожидание каких-то аппаратных событий, например, тех же данных на COM порту.


        Ну попробую еще раз объяснить ... :wall: Как-то недавно Киля мне порекомендовал либу Interception. Процесс чтения данных там можно осуществлять двумя функциями (вернее ожидание данных на устройстве):

        1) interception_wait
        2) interception_wait_with_timeout

        Так вот первую функцию я не могу использовать по одной простой причине - пока она бесконечно висит я не могу завершить поток нормальным способом если вдруг мне это надо. Поэтому я использую вторую функцию с указанием тайм аута. Нет данных - пошла очередная итерация цикла. А вот в ней я уже могу анализировать BreakFlag. Если увижу что взведен - выхожу из цикла и завершаю поток.

        Советовали и мьютексы, и семафоры, и cоndition_variable. Но куда тут их всунуть? Кто извне (из другого треда) будет их забирать-отпускать?

        А вот теперь, допустим, другая похожая задача.

        Я должен осуществлять другую операцию в потоке - вместо interception_wait_with_timeout ... что нибудь типа std::filesystem::exists("helloworld.txt"). И если файл найден - удалять. Делать пока поток не будет прерван. Если sleep в цикл не поставлю - "комп через час-два закипит"?
          JoeUser, давай пройдёмся по теории. Её тут уже коснулись, но систематизировать не помешает.
          Итак, существует два варианта реакции на изменившиеся окружающие условия: либо тебе об этом кто-то сообщает, и тогда нет проблем, вешаешься на сигнал и ждёшь; либо никаких нотификаций не предусмотрено, тогда у тебя остаётся вариант пуллить это событие самостоятельно. Второй вариант, вообще говоря, сводится к первому, надо лишь самому реализовать эту нотифю, вопрос лишь в том, стоит ли. Но как бы там ни было, в первом случае задача решена до тебя, и этот вариант уже рассматривался выше, но по ходу это не тот вариант, который тебя интересует, так что второй.
          Любой пуллинг основан на периодическом опросе. Вопрос о его реализации, думаю, стоит не в том ключе, мол, как это сделать, а в том, мол, как это сделать эффективно. Wound тебе предложил условные переменные, что даёт половину решения, основанное на нотификациях, условия которых ты сам же и определяешь. Не самое удобное, возможно, зато универсальное: написал один раз и забыл. Вторая половина упирается в то, что нельзя ничего сказать о величине периода опроса, т.к. это сильно зависит от характера процесса, чьё состояние опрашивается. И от него же зависит формулировка условия для нотификации.
          К примеру, опрос своего файлика ты можешь оформить просто как
          ExpandedWrap disabled
            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"); });
          Как бы с этим проблем нет. Точное время опроса и точных критериев нотификации тебе никто в общем случае не подскажет.
          Проблема же всплывает, если тебе нужно отслеживать несколько нотификаций сразу и по-разному на них реагировать. Ну так выше я уже писал, что условие ты можешь поставить любое. Напиши что-то типа
          ExpandedWrap disabled
            std::atomic<bool> break_event = false;
            /* ... */
            cv.wait_for(lk, 1s, []{ return std::filesystem::exists("helloworld.txt") || break_event; });
          И снова никто не подскажет общего случая, условия могут быть самыми разными.

          P.S. В принципе при таком подходе уже и таймаут не нужен.
          Сообщение отредактировано: Qraizer -
            Цитата JoeUser @
            Советовали и мьютексы, и семафоры, и cоndition_variable. Но куда тут их всунуть? Кто извне (из другого треда) будет их забирать-отпускать?

            А ранее ты сам же на этот вопрос и отвечаешь :D :
            Цитата JoeUser @
            Так вот первую функцию я не могу использовать по одной простой причине - пока она бесконечно висит я не могу завершить поток нормальным способом если вдруг мне это надо. Поэтому я использую вторую функцию с указанием тайм аута. Нет данных - пошла очередная итерация цикла. А вот в ней я уже могу анализировать BreakFlag. Если увижу что взведен - выхожу из цикла и завершаю поток.

            На сколько я понял у тебя это как раз взаимосвязано. Там где тебе нужно поменять флаг - там ты пишешь notify_one/notify_all, там где у тебя флаги проверяются - ты пишешь wait/wait_for/wait_until/etc.
            У тебя по условию задачи - есть сигнал и есть его обработчик. Сейчас ты это все разруливаешь флагами, у тебя в каком то месте флагу присваивается значение true, а в другом месте этот самый флаг на значение true проверяется. Тебе в том месте - где присваивается значение флагу true - нужно изменить условие(поменять значение на true своему флагу например) и вызвать notify, а в том месте - где у тебя флаг проверяется тебе нужно заюзать wait_for. Что тут непонятного то? :-?
            Сообщение отредактировано: Wound -
              Qraizer, да, именно о пуллинге идет речь. потому как не от каждой операционной системы и не по каждому состоянию получишь какое либо сообщение.
              Вот я накидал пример того, о чем спрашиваю:
              ExpandedWrap disabled
                #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%
                Вот например есть какой то синтетический пример, показывающий как это все работает: https://ru.cppreference.com/w/cpp/thread/co...riable/wait_for
                ExpandedWrap disabled
                  #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();
                  }
                  Цитата Wound @
                  На сколько я понял у тебя это как раз взаимосвязано. Там где тебе нужно поменять флаг - там ты пишешь notify_one/notify_all, там где у тебя флаги проверяются - ты пишешь wait/wait_for/wait_until/etc.

                  Киля, вон я выше накидал тестовую прогу. К тебе тож просьба - перепиши ее с твоими wait/async/future, ну как ты советовал.

                  Добавлено
                  Цитата Wound @
                  Вот например есть какой то синтетический пример

                  Мой перепиши, плс, то что мне надо.
                    Цитата JoeUser @
                    Прога элементарная. Ждет написания в консоли слова Exit и нажатие [Enter]. А пока ждет - работает. А именно ищет файл "C://Temp//helloworld.txt" и если находит - грохает его.

                    Перепишите мне с мьютексами, семафорами, или condition_variable чтобы стало лучше/правильнее. И что именно станет правильнее и лучше?

                    А причем тут эта прога и то что ты хочешь? Ты пишешь - как мне усыпить поток, чтоб он мне CPU не жрал. И просишь переписать твой пример - где у тебя усыплением потока и не пахнет, где один поток файлы ищет, а второй ждет пока ему Exit не напишут. Как ты свой пример перепишешь с WaitForMultiplyObjects ? Я не понимаю твоего примера.

                    Добавлено
                    Цитата JoeUser @
                    Мой перепиши, плс, то что мне надо.

                    Не могу. У тебя приведенный пример, противоречит тому, что ты тут две страницы пояснял. Я не знаю что тебе тут переписать? В примере приведеном тобою главный поток ждет, пока ему не напишут Exit, а потом тупо выходит.
                    Или тебе нужно застопорить поток там, где у тебя std::this_thread::yield(); ?
                    Тогда как то так будет:
                    ExpandedWrap disabled
                      #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;
                      }
                      Цитата Wound @
                      Ты пишешь - как мне усыпить поток

                      Да ё-мое! Ну почитай ты название топика! :( Я спрашивал что нужно чтобы прога не жрала CPU без надобности и только. А то что я усыпляю его на доли секунды - это как одно из решений. Я не спрашиваю как его полностью усыпить!

                      Добавлено
                      Цитата Wound @
                      Я не понимаю твоего примера.

                      Я старался! :no-sad:
                        Цитата JoeUser @
                        Да ё-мое! Ну почитай ты название топика! :( Я спрашивал что нужно чтобы прога не жрала CPU без надобности и только. А то что я усыпляю его на доли секунды - это как одно из решений. Я не спрашиваю как его полностью усыпить!

                        В каком месте у тебя прога жрет CPU без надобности?
                        Ты в первом посту пишешь:
                        Цитата JoeUser @
                        В одной из программ, которую подогнали разработчики, постоянная загрузка CPU висит в районе 25%. И это на Core i3 2.5GHz. А программа всего лишь что и делает в фоне - только мониторит состояние фискального аппарата (если я не ошибаюсь по COM-порту). Все мои попытки убедить разработчиков, что это нонсенс, упираются в "стену". Отвечают что-то в роде "нам важно, чтобы система с фискальным аппаратом работала без задержек, мы лучше знаем" :wall: Я подозреваю, что залепили в своем коде что-то в виде:

                        Где в твоем примере мониторинг состояния, любого?
                          Цитата Wound @
                          Тогда как то так будет:

                          И улучшений - нуль! :lol:
                          Если слип-задержку не раскоментить - как жрала прога 17% CPU, так и жрет. Это сразу было видно по твоему коду. Но я проверил на всяк случай:

                          user posted image
                            Цитата JoeUser @
                            А именно ищет файл "C://Temp//helloworld.txt" и если находит - грохает его.

                            Перепишите мне с мьютексами, семафорами, или condition_variable чтобы стало лучше/правильнее. И что именно станет правильнее и лучше?

                            Эта задача правильнее и лучше не решается.
                            Ты не можешь подписаться в системе на такое событие - "наличие такого-то файла".
                            А вот следить за изменениями в указанной директории вроде можно.
                            (я этого не делал, поэтому определённо сказать не могу).
                            тут
                            Поэтому заснуть потоком, но ждать событие "изменения в папке" - можно.
                            ---
                            И потом - что значит "правильнее и лучше" ?
                            Это всё субъективно.
                            Для того, что ты показал, сделанное достаточно.
                            Но если бы ты задавал имя файла для поиска и уничтожения
                            в окошке эдита, а потом жал на кнопку, тогда надо:
                            1. Заснуть потоком на Wait.
                            2. Ждём 2 события - Exit и Work.
                            3. Если завершаем приложение, взводим Exit
                            4. Если жмём на кнопку "Уничтожить указанный файл" - значит
                            взводим Work. Дальше начинается поиск файла в папке и всех вложенных папках
                            и уничтожение его. Потом снова засыпаем.
                            Сообщение отредактировано: ЫукпШ -
                              Цитата Wound @
                              Где в твоем примере мониторинг состояния, любого?

                              Вот тут:
                              ExpandedWrap disabled
                                if (std::filesystem::exists(FileName))
                                Цитата ЫукпШ @
                                Ты не можешь подписаться в системе на такое событие - "наличие такого-то файла".

                                Ну в разных ОС по разному, в Линухе, к примеру, это можно решить с помощью inotify.
                                Там много чего можно отслеживать, в том числе и появление файла, и его закрытие после записи.
                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                0 пользователей:
                                Страницы: (4) 1 [2] 3 4  все


                                Рейтинг@Mail.ru
                                [ Script execution time: 0,0590 ]   [ 16 queries used ]   [ Generated: 29.03.24, 09:23 GMT ]