C++, usleep() устарел, обходные пути для Windows/MingW?

Я уже выяснил с другим вопросом, что Windows/MingW не предоставляет альтернативы nanosleep () и setitimer() устаревшему usleep(). Но моя цель - исправить все предупреждения, которые дает мне cppcheck, включая предупреждения в стиле usleep().

Итак, есть ли обходной путь, чтобы как-то избежать usleep () в Windows без использования cygwin или установки множества новых зависимостей / библиотек? Благодарю.

6 ответов

Решение

usleep() работает с микросекундами. В окнах для получения приоритета микросекунды вы должны использовать функцию Winapi QueryPerformanceCounter(). Здесь вы можете найти, как получить это преимущество, используя его.

Я использовал этот код (изначально отсюда):

#include <windows.h>

void usleep(__int64 usec) 
{ 
    HANDLE timer; 
    LARGE_INTEGER ft; 

    ft.QuadPart = -(10*usec); // Convert to 100 nanosecond interval, negative value indicates relative time

    timer = CreateWaitableTimer(NULL, TRUE, NULL); 
    SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0); 
    WaitForSingleObject(timer, INFINITE); 
    CloseHandle(timer); 
}

Обратите внимание, что SetWaitableTimer() использует " 100 наносекундных интервалов... Положительные значения указывают абсолютное время.... Отрицательные значения указывают относительное время. " и что " Фактическая точность таймера зависит от возможностей вашего оборудования ".

Если у вас есть компилятор C++11, вы можете использовать эту переносимую версию:

#include <chrono>
#include <thread>
...
std::this_thread::sleep_for(std::chrono::microseconds(usec));

Престижность Говарду Хиннанту, который разработал удивительный <chrono> библиотека (и чей ответ ниже заслуживает большей любви.)

Если у вас нет C++11, но у вас есть повышение, то вы можете сделать это вместо этого:

#include <boost/thread/thread.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
...
boost::this_thread::sleep(boost::posix_time::microseconds(usec));

Новый ответ на старый вопрос:

Обоснование нового ответа: Инструменты / ОС были обновлены так, что теперь есть лучший выбор, чем был, когда был задан вопрос.

C++11 <chrono> а также <thread> Заголовки std находятся в наборе инструментов VS уже несколько лет. Используя эти заголовки, это лучше всего кодируется в C++11 как:

std::this_thread::sleep_for(std::chrono::microseconds(123));

Я использую микросекунды только в качестве примера продолжительности. Вы можете использовать любое удобное время:

std::this_thread::sleep_for(std::chrono::minutes(2));

С C++14 и некоторыми использующими директивами это можно записать немного более компактно:

using namespace std::literals;
std::this_thread::sleep_for(2min);

или же:

std::this_thread::sleep_for(123us);

Это определенно работает на VS-2013 (по модулю хроно-литералов). Я не уверен насчет более ранних версий VS.

Миллисекундный режим Sleep() Функция хорошо описана и понятна. Это не делает ничего непредсказуемого. Иногда функцию обвиняют в непредсказуемости, т. Е. В возврате до истечения задержки. Я должен сказать, что это неправильно. Тщательное расследование подтвердит, что его поведение абсолютно предсказуемо. Единственная проблема в том, что об этом есть что почитать, и большинство из них - детское. Также часто говорят, что Windows это не ОС реального времени. Но такие комментарии ничего не дают, более того, такие комментарии используются, чтобы скрыть недостаток знаний. Меня это бесит, что даже Microsoft не замечает этого и не предоставляет лучшую документацию.

Однако, не преувеличивая этого небольшого ответа: функция sleep() является точной, если она используется надлежащим образом и когда она знает свои характеристики. Особое внимание должно быть уделено сну (0). Это очень мощный инструмент, особенно при использовании вместе с классом приоритета процесса, приоритетом потока, настройками мультимедийного таймера и маской привязки процессора.

Поэтому, как правило, настоящий сон может выполняться легко и безопасно вплоть до периода прерывания работы системы. Когда речь идет о снах, которые короче, чем период прерывания, требуется вращение. Источник времени с более высоким разрешением должен использоваться для того, чтобы вращаться в течение более коротких периодов времени. Наиболее распространенным источником для этого является счетчик производительности. QueryPerformanceCounter(*arg) поставляет инкрементальный * аргумент QueryPerformanceFrequency(*arg) обеспечивает частоту, с которой увеличивается счетчик производительности. Обычно это режим МГц и варьируется в зависимости от используемого оборудования. Частота в диапазоне МГц обеспечивает микросекундное разрешение. Таким образом, что-то с высоким разрешением может быть использовано для ожидания истечения желаемого промежутка времени. Тем не менее, к точности этого нужно подходить внимательно: ОС возвращает частоту счетчика производительности как постоянную. Это не верно! Поскольку частота генерируется физическим устройством, всегда есть смещение, и оно также не является постоянной величиной. Имеет термический дрейф. Более современные системы имеют меньший дрейф. Но если температурный дрейф составляет всего 1 стр / мин, ошибка составит 1 мкс / с. Смещение может легко составлять несколько 100. Смещение 100 в 1 МГц соответствует 100 мкс / с.

Если поток должен ждать какое-то время с высоким разрешением, он должен установить поток службы. Оба потока должны совместно использовать именованное событие. Служебный поток должен находиться в режиме ожидания до 1 периода прерывания до желаемой задержки в режиме ожидания, а затем вращаться на счетчике производительности в течение оставшейся микросекунды. Когда служебный поток достигает последнего времени, он устанавливает указанное событие и завершается. Вызывающий поток проснется, потому что он ожидал именованного события с помощью функции ожидания.

Резюме:

  • Сон хорошо понят, но плохо документирован.
  • Служебная нить может имитировать сны с высоким разрешением.
  • Такой сервисный поток может быть признан общесистемным сервисом.
  • Точность счетчика производительности следует внимательно изучить. Требуется калибровка.

Более подробную информацию можно найти в Windows Timestamp Project.

Это зависит от того, какая гранулярность вам нужна. Если вы говорите миллисекунды, то функция Win32 Sleep сделает эту работу - см. http://msdn.microsoft.com/en-us/library/ms686298%28v=vs.85%29.aspx. Если вы говорите микросекунды, то нет простого способа сделать это, и вам посчастливится получить такое разрешение таймера в Windows (которая не является RTOS) или в Linux.

Я нашел этот пост в блоге об этом. Оно использует QueryPerformanceCounter, Функция размещена:

#include <windows.h>

void uSleep(int waitTime) {
    __int64 time1 = 0, time2 = 0, freq = 0;

    QueryPerformanceCounter((LARGE_INTEGER *) &time1);
    QueryPerformanceFrequency((LARGE_INTEGER *)&freq);

    do {
        QueryPerformanceCounter((LARGE_INTEGER *) &time2);
    } while((time2-time1) < waitTime);
}

Надеюсь, это немного поможет.

Я довольно опаздываю на вечеринку, но хочу просто добавить кое-что к этому вопросу. Если вы хотите добиться переносимости с использованием разрешения в микросекунды, используйтеselect()системный вызов с использованием пустого набора дескрипторов файла. Он будет работать как в Linux, так и в Windows, т.е. его можно вызывать с помощью единого интерфейса (поведение может быть другим, особенно в Windows, где вы можете запросить 1 микросекунду, но получите 1 мс сна). Если вы хотите использовать стороннюю библиотеку, используйте Boost, но последнюю версию. Связанный со временем std api - это просто беспорядок, и я привожу здесь сводку:

  1. Windows: sleep_for, sleep_until, condition_variable, future и т.д. методы wait_for/wait_until полностью ненадежны, потому что они основаны на system_clock, поэтому при изменении времени ваше приложение может зависнуть. Они исправили эту проблему внутренне, но, поскольку это перерыв в ABI, они еще не выпустили ее, недоступную даже в последней версии VS 2019.
  2. Linux: sleep_for, sleep_until в порядке, но condition_variable/future wait_for/wait_until ненадежны, потому что они также основаны на системных часах. Это исправлено с помощью ссылки на ошибку Gcc 10 и GlibC 2.6.
  3. Boost: То же, что и выше, но они исправили проблемы со временем в версии 1.67.

Поэтому, если вы хотите, чтобы переносимый код создавал свое собственное решение или просто использовал Boost 1.67+, не доверяйте стандартной реализации C++11 chrono, потому что выполненная реализация действительно плохая.

Другие вопросы по тегам