На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: Qraizer, Hsilgos
Страницы: (4) [1] 2 3 ... Последняя » все  ( Перейти к последнему сообщению )  
> Разгрузка CPU в потоке
    Всем привет!

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

    ExpandedWrap disabled
      do {
        if (BreakFlag) break;
        if (!ReadyFlag) continue;
        doSomething();
        ...
      } while(true);

    Ну Ктулху - им судья! :no-sad: А вот теперь мне в своей проге понадобилось сделать что-то подобное. Понимаю, что идеальным вариантом можно было бы использовать WaitForMultipleObjects. Но для кросс-платформенной реализации такой вариант - не выход. А похожей блокирующей функции у меня в распоряжении нет. Есть только флаг готовности. Вот собственно вопрос - на сколько я повторю ситуацию, описанную в начале поста, если это реализую подобным образом, но с усыплением потока? Что-то типа:

    ExpandedWrap disabled
      do {
        if (BreakFlag) break;
        if (!ReadyFlag) {
          std::this_thread::sleep_for(1ms);
          continue;
        }
        doSomething();
        ...
      } while(true);


    Какие варианты есть лучше?
      Цитата JoeUser @
      Какие варианты есть лучше?

      Лучше тебе предлагали в прошлой теме, но ты их принял в штыки, а оппонентов выставил дураками. :-?
      Вангую тут будет ровно тоже самое.
        Если ты про тему "Распараллеливание выполнения без блокировок", то там акцент был не про сабж, а скорее о параллельном доступе к данным.

        Ну а твой совет типа:

        Цитата Wound @
        1) Вместо std::thread - используй std::future в связке с async (это следует делать в 95% случаях, по крайней мере так советует Майерс, и я ему верю, хотя некоторые не согласны с этим утверждением, но все же) - потому что std::thread имеют кучу подводных камней(читай Майерса)


        Я вообще не пойму как использовать для опроса устройства. На каждое считывание запускать заново поток? И что это даст?
        И второй момент, а как мне прервать такой поток, если ты мне далее предлагаешь

        Цитата Wound @
        2) Вместо std::atomic<ololo> flag - используй conditial variables.

        Мне ведь два состояния нужно отслеживать - чтение и прерывание цикла чтения. Я загуглил, на SO ответили однозначно, что нужен велосипед (You can't, and must redesign), т.к. conditial variable только одно состояние отслеживает.
          Цитата JoeUser @
          .. Есть только флаг готовности... Что-то типа:

          ExpandedWrap disabled
            do {
              if (BreakFlag) break;
              if (!ReadyFlag) {
                std::this_thread::sleep_for(1ms);
                continue;
              }
              doSomething();
              ...
            } while(true);


          Какие варианты есть лучше?

          В качестве флагов можно использовать семафоры.
          Которые есть и у Виндус, и у Юникс.
          Значит, можно сделать до некоторой степени аналог WaitForMultipleObjects.
          Те сделать функцию, которая ждёт много семафоров.
          И по включению каждого вызывает конкретный обработчик.
          Либо использовать кросс-платформенную библиотеку.
          Где всё это должно быть.
          Сообщение отредактировано: ЫукпШ -
            Цитата JoeUser @
            Какие варианты есть лучше?

            Что мешает читать из COM порта с таймаутом? Как данные придут, то функция сразу вернет управление.
            Второй вариант: использовать асинхронное API для работы с COM портом. Бонус: не нужен даже доп. поток.

            Добавлено
            По первому варианту SetCommTimeouts в помощь. Если прямо по байту хочешь вычитывать, то ReadIntervalTimeout ставишь в 1. ReadTotalTimeoutConstant во что-нибудь адекватное, чтобы не завис поток, если данных нет.

            Добавлено
            По второму тоже примеров в сети куча. Смысл в том, что ты будешь висеть на WaitForMultipleObjects пока не придут данные.

            Добавлено
            Цитата JoeUser @
            Отвечают что-то в роде "нам важно, чтобы система с фискальным аппаратом работала без задержек, мы лучше знаем"

            Вызов ClearCommError в бесконечном цикле никак не улучшит отклик.

            Добавлено
            Цитата JoeUser @
            Но для кросс-платформенной реализации такой вариант - не выход.

            boost::asio, только за неимением возможности установить таймаут на порт у тебя нет альтернатив, кроме как использовать асинхронные функции
            Сообщение отредактировано: shm -
              Цитата JoeUser @
              Я вообще не пойму как использовать для опроса устройства. На каждое считывание запускать заново поток? И что это даст?
              И второй момент, а как мне прервать такой поток, если ты мне далее предлагаешь

              Во первых твоя задача не ясна. Я лично вообще не понимаю что ты делаешь. Ты привел кусок кода - и говоришь: как мне его нужно переписать, чтоб было правильно. Из твоего кода ясно что у тебя есть два флага - флаг готовности и флаг завершения программы.
              За что отвечает флаг готовности? Флаг готовности для чего? Как он взаимосвязан с остальной логикой? Может быть этот флаг означает что очередь не пуста? Или он означает что произошло какое то событие? События реализуются с помощью conditional_variable, ну как вариант, у них есть свои достоинства и недостатки. Помимо conditional_variable, можно использовать get_future().wait/std::promise::set_value, но эта штука одноразовая, в отличии от CV.
              Еще ты можешь получить хендл потока std::thread::native_handle() и юзать WaitForMultiplyObjects.

              Например какой то пример эмуляции WaitForMultiplyObjects:
              http://coliru.stacked-crooked.com/a/1dfb87728994b184
              ExpandedWrap disabled
                #include <iostream>
                #include <vector>
                #include <thread>
                #include <future>
                #include <chrono>
                 
                const int TIME_BETWEEN_POLLS_MS = 50;
                 
                // Wait (sleep) between polls until a task is finished then return index.
                template <typename Iterator>
                Iterator finishingThread(Iterator first, Iterator last) {
                    auto it = first;
                    auto status = std::future_status::timeout;
                    while (status != std::future_status::ready) {
                        if (++it == last) {
                            it = first;
                        }
                        status = it->wait_for(std::chrono::milliseconds(TIME_BETWEEN_POLLS_MS));
                    }
                    return it;
                }
                 
                // Example task function that sleeps for specified time and returns thread id.
                std::thread::id sleepFor(int millisec) {
                    std::this_thread::sleep_for(std::chrono::milliseconds(millisec));
                    std::cout << "Terminating thread with id: " << std::this_thread::get_id() << std::endl;
                    return std::this_thread::get_id();
                }
                 
                int main() {
                    // Create three separate tasks.
                    std::packaged_task<std::thread::id(int)> task1{sleepFor},
                                                             task2{sleepFor},
                                                             task3{sleepFor};
                 
                    // Store futures.
                    std::vector<std::future<std::thread::id>> futures;
                    futures.push_back(task1.get_future());
                    futures.push_back(task2.get_future());
                    futures.push_back(task3.get_future());
                 
                    // Run tasks on separate threads.
                    std::vector<std::thread> tasks;
                    tasks.emplace_back(std::move(task1), 1500);
                    tasks.emplace_back(std::move(task2), 500);
                    tasks.emplace_back(std::move(task3), 1000);
                 
                    auto it = finishingThread(std::begin(futures), std::end(futures));
                 
                    std::cout << "We have result of the task that finished first!" << std::endl;
                 
                    // Join threads.
                    for (auto& t : tasks) {
                        t.join();
                    }
                 
                    std::cout << "Winner result: " << it->get() << std::endl;
                }
              Сообщение отредактировано: Wound -
                Цитата ЫукпШ @
                В качестве флагов можно использовать семафоры.

                А чем хуже std::atomic?

                Добавлено
                shm, сорь! Чтение из COM-порта я в начале привел для примера. А так я работаю с USB с помощью либы, которую мне присоветовал Киля. Там есть хорошая функция interception_wait_with_timeout и она решает мою проблему (т.к. в глубине либы она завязана на WaitForMultiplyObjects с тайм аутом).
                У меня же был вопрос более общий - как отдавать кванты вычислений не привязываясь к API Win32 если используется неблокирующая обработка "любого толка"?
                  Цитата JoeUser @
                  Цитата ЫукпШ @
                  В качестве флагов можно использовать семафоры.

                  А чем хуже std::atomic?

                  Это не о том - насколько я понимаю.
                  я не про атомарность говорю, а отвечаю на вопрос по теме - как экономить время.
                  Для этого надо работать по событиям.
                  У тебя 2 события в цикле - "выход" и "работа".
                  Значит надо остановить поток и ждать 2 объекта, я предлагаю семафоры.
                  -----
                  Семафор - это такой объект ядра, который может остановить поток.
                  И этот поток перестанет тратить процессорное время вообще.
                  Кроме того, у него имеется полезное свойство - если будет
                  получено несколько (много) уведомлений о событии, в то время
                  как текущее событие всё ещё обрабатывается, ни одно
                  из уведомлений не будет утеряно.
                  Рабочий поток будет разбужен семафором столько раз, сколько
                  надо.
                    Цитата Wound @
                    Во первых твоя задача не ясна. Я лично вообще не понимаю что ты делаешь.

                    А как можно начинать советовать, если не выяснил? :blink:
                      Цитата JoeUser @
                      А как можно начинать советовать, если не выяснил?

                      А я ниче и не советовал. Просто посетовал, что опять будешь всех дураками выставлять :-?
                      Сообщение отредактировано: Wound -
                        Цитата ЫукпШ @
                        Значит надо остановить поток и ждать 2 объекта, я предлагаю семафоры.

                        Я тебя понял! Только тут получается какой-то замкнутый круг ... Вот представь пример. Unix система. Второй поток, который висит "на семафорах" должен распечатать файл в STDOUT, если файл он появился в каталоге. Ну или должен завершиться, если завершается вся программа. Получается, что должен быть еще один поток (третий), который уведомит второй поток, что файл появился. А в нем как разгружать CPU в ожидании?

                        Да, я знаю, что существует интересная либа libevent, которая эмулирует кросс-платформенно систему уведомлений (сообщений). Но вопрос был в другом - как стандартными средствами C++11 и выше разгружать CPU?
                          Скрытый текст
                          Цитата Wound @
                          А я ниче и не советовал. Просто посетовал, что опять будешь всех дураками выставлять

                          Киля, ты всегда рвешься в бой быстрее танка. Начинаешь сразу рубить с плеча, или обижаться. Пойми, я никак не принижаю твои достоинства как программера, и очень часто тебе благодарен. Но я считаю, что ты очень часто спешишь не разобравшись. А ведь не всегда удается задать вопрос так, чтобы непоняток не было вообще ... если вопрос конечно ну не совсем уж тривиальный. Без обид! :)
                            Цитата JoeUser @
                            Unix система. Второй поток, который висит "на семафорах" должен распечатать файл в STDOUT, если файл он появился в каталоге. Ну или должен завершиться, если завершается вся программа. Получается, что должен быть еще один поток (третий), который уведомит второй поток, что файл появился. А в нем как разгружать CPU в ожидании?

                            1 Про выход - если приложение завершается, значит будет вызван
                            деструктор объекта-потока. Из деструктора будет установлен
                            семафор "Exit", а потом ожидание завершения потока (pthread_join).
                            Рабочий поток завершиться по семафору. На случай проблем
                            предусмотрим принудительное уничтожение потока, если будет
                            превышено время допустимого ожидания завершения.

                            2. Если ты что-то там ожидаешь в событийной модели, значит
                            копай доки для твоего случая.
                            Если нет таких событий - тогда никак. Тогда надо ждать в цикле со Sleep-ом.
                            И семафор тоже можно опрашивать в таком цикле.
                            --
                            Кроме того, у Юникса есть функции ожидания - Select, например.
                            Работает с файловыми дескрипторами.
                            Скрытый текст

                            На просторах Сети я узнал про такой финт ушами:
                            - для создания события на выход из цикла был создан pipe и его
                            хэндл тоже был передан Select-у. Для выхода достаточно было
                            послать символ в pipe.


                            Надо заметить, что цикл со Sleep-ом это не криминал.
                            Однако, чем меньше будет таких решений, тем меньше
                            будет бессмысленно истраченного времени.
                            Сообщение отредактировано: ЫукпШ -
                              Цитата JoeUser @
                              А в нем как разгружать CPU в ожидании?

                              Да, я знаю, что существует интересная либа libevent, которая эмулирует кросс-платформенно систему уведомлений (сообщений). Но вопрос был в другом - как стандартными средствами C++11 и выше разгружать CPU?

                              Вот в этом и заключается преимущество подхода на основе conditional variable. Они не нагружают CPU, и реально ждут оповещения(плюс нужно учитывать что случаются ложные пробуждения, но они очень просто лечатся).
                              Т.е. они работают как события и при этом не грузят аппаратный поток, как в случае с флагами. Ровно так же работают промисы, но у них есть одна ложга дегтя, их можно установить только 1 раз, в отличии от conditional variable.


                              Цитата JoeUser @
                              Киля, ты всегда рвешься в бой быстрее танка. Начинаешь сразу рубить с плеча, или обижаться. Пойми, я никак не принижаю твои достоинства как программера, и очень часто тебе благодарен. Но я считаю, что ты очень часто спешишь не разобравшись. А ведь не всегда удается задать вопрос так, чтобы непоняток не было вообще ... если вопрос конечно ну не совсем уж тривиальный. Без обид!

                              Так задай его так, чтоб было понятно на него отвечать.
                              Сообщение отредактировано: Wound -
                                Цитата JoeUser @
                                У меня же был вопрос более общий - как отдавать кванты вычислений не привязываясь к API Win32 если используется неблокирующая обработка "любого толка"?

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

                                Добавлено
                                Короче мне сама формулировка "отдать квант" непонятна.

                                Добавлено
                                Есть еще такая замечательная функция как std::this_thread::yield(). С помощью нее ты говоришь ОС "моему потоку сейчас делать нечего, пускай поработают другие". При этом, если в ОС есть потоки не в спячке, то им реально будет передано управление. Если таких нет, то ты получишь управление сразу же обратно и как следствие та же загрузка ядра на максимум. Считается, что наличие yield() и подобных функций в коде - это признак кривой архитектуры.
                                Сообщение отредактировано: shm -
                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                0 пользователей:


                                Рейтинг@Mail.ru
                                [ Script execution time: 0,0591 ]   [ 17 queries used ]   [ Generated: 29.03.24, 14:24 GMT ]