Вопрос по multithreading, winapi, loops, c++, sleep – Точная нить нужна. Макс 1 мс ошибка

8

У меня есть нить, которая запускает цикл. Мне нужно, чтобы этот цикл запускался один раз каждые 5 мс (ошибка 1 мс). Я знаю, что функция Sleep () не является точной.

У вас есть какие-нибудь предложения?

Обновить. Я могу'сделать это по-другому. В конце цикла мне нужен какой-то сон. Я неНе хочу загружать процессор на 100%.

@JohnDibling: они просили ошибку в 1 мс дляSleep() задержка. Тот'не слишком сложно получить. И они также неиспользуйте слово вместе со спецификацией ошибки. Какие'здесь противоречиво? Arno
@DavidSchwartz: переключение контекста обычно выполняется за несколько десятков микросекунд. Система может 't полезная работа за 5 мс? Arno
Точность около 1 мс немного оксюморон John Dibling
@Arno: в заголовке указана ошибка 1 мс, а в вопросе указана продолжительность 5 мс. Тот'ошибка 20%. В моей книге этоне очень точно. John Dibling

Ваш Ответ

5   ответов
3

вместо использования sleep вы можете попробовать цикл, который проверяет временной интервал и возвращает значение, когда разница во времени составляет 5 мс. Цикл должен быть более точным, чем сон.

Однако следует помнить, что точность не всегда возможна. Процессор может быть связан с другой операцией за такой небольшой интервал и может пропустить 5 мс.

Это вариант. Но я бы хотел дать процессору отдохнуть 5 мс. Hooch
В самом деле; за этот промежуток времени можно включить несколько потоков.blog.tsunanet.net/2010/11/... Rook
Да, может быть, я старая школа, но это может случиться, процессор делает что-то еще и пропускает проверку 1 мс. Он должен быть проверен под нагрузкой и т. Д., Если требуется 1 мс. Kami
5мс это неочень небольшой интервал, хотя xD SingerOfTheFall
1

CreateWaitableTimerSetWaitableTimerWaitForSingleObject

Позвольте вам создать ожидаемый таймер с разрешением 100 нс, дождаться его и заставить вызывающий поток выполнить определенную функцию во время запуска.

Вот'Пример использования указанного таймера.

Обратите внимание, что у WaitForSingleObject есть тайм-аут, измеренный в миллисекундах, который, возможно, мог бы служить грубой заменой ожидания, но я бы не сталне могу доверять этому. Смотрите это ТАКвопрос для деталей.

4

Мультимедийные таймеры, они рекламируют точность до 1 мс. Другой вариант заключается в использованииСпин-замки но это в основном сохранит ядро процессора при максимальном использовании.

@ShadowRanger: яЯ в замешательстве. Вы, кажется, согласны с тем, что я написал, но пишете так, как будто этоС опровержением. Adrian McCarthy
На самом деле, они не рекламируют точность менее 1 мс. Вы должны запросить поддерживаемый диапазон периодов, а затем использовать timeBeginPeriod для чего-то в этом диапазоне. Поскольку timeBeginPeriod принимает значение в миллисекундах, маловероятно, что вы могли бы сделать лучше, чем 1 мс. Да, и ускорение закрытия системы с помощью timeBeginPeriod отрицательно влияет на производительность системы и энергопотребление, поэтому обязательно вызовите timeEndPeriod, как только вам больше не понадобится эта точность. Adrian McCarthy
И учитывая, чтоtimeBeginPeriod а такжеtimeEndPeriod По-видимому, функции изменяют глобальное состояние ОС (не только для вашего собственного процесса), а документы, по-видимому, подразумевают, чтоtimeBeginPeriod это нене соответствуетtimeEndPeriod ISN»кажется, что даже после смертидействительно легко (например, вызвать сбой или иным образом жестко убить процесс во время настройки часов), чтобы случайно окончательно перевести системные часы в неоптимальное состояние навсегда (или, по крайней мере, до перезагрузки). Действительно плохо для всего, что работает от батареи, где больно повышенное энергопотребление. Безразлично»в общем, хорошая идея ShadowRanger
@AdrianMcCarthy: кромесвои собственные документы на "Функции ожидания и интервалы ожидания утверждать, что "Если вы позвонитеtimeBeginPeriod, позвоните один раз в начале заявки и обязательно позвонитеtimeEndPeriod функция в самом конце приложения " так как "частые звонки могут значительно повлиять на системные часы, энергопотребление системы и планировщик ", Так что если выв зависимости от этой точности для многих вызовов, вы не должныОтрегулируйте до и после каждого вызова. ShadowRanger
10

а также Точность может быть достигнута стандартными методами.

Вы можете использоватьSleep() до периодов около 1 мс, когда период прерывания системы установлен для работы на этой высокой частоте. Посмотрите наописание сна () чтобы получить подробности, в частности,мультимедийные таймеры сПолучение и настройка разрешения таймера получить подробную информацию о том, как установить период прерывания системы. Доступныйточность при таком подходе это в диапазоне нескольких микросекунд при правильной реализации.

Я подозреваю, что твой круг делает что-то еще. Таким образом, я подозреваю, что вы хотите общий период 5 мс, который будет суммойSleep() и остальное время вы тратите на другие вещи в цикле.

Для этого сценария ябуду предлагатьОжидание Таймер Объектыоднако эти таймеры также полагаются на настройку API мультимедийного таймера. Я'Мы дали обзор соответствующих функций для более точной синхронизацииВот, Можно найти гораздо более глубокое понимание высокой точности синхронизацииВот.

Для еще более точного и надежного выбора времени вам может понадобитьсяprocess priority classes а такжеthread priorities, Еще один ответ о точности Sleep ()этот.

Тем не менее, возможно ли получитьSleep() задержка в 5 мс зависит от аппаратного обеспечения системы. Некоторые системы позволяют работать с 1024 прерываниями в секунду (устанавливается с помощью API мультимедийного таймера). Это соответствует периоду 0,9765625 мс. Ближайшее, что вы можете получить, это 4.8828125 мс. Другие позволяют приблизиться, особенно после Windows 7, время значительно улучшилось при работе на аппаратном обеспечении.high resolution event timers, УвидетьО таймерах на MSDN иВысокоточный таймер событий.

Резюме: Установите мультимедийный таймер для работы на максимальной частоте и использованияТаймер ожидания

Я посмотрю на это. Благодарю. Hooch
7

которая подходит для приложений реального времени (то есть высокое разрешение / высокая точность с надежностью). Вот мои выводы:

Основы планирования

Отказ от процессора, а затем вернуть егодорого, В соответствии сЭта статьязадержка планировщика может быть где-то между 10-30 мс в Linux. Поэтому, если вам нужно спать с точностью менее 10 мс с высокой точностью, вам нужно использовать специальные API для конкретных ОС. Обычный C ++ 11 std :: this_thread :: sleep_for - это не сон в высоком разрешении. Например, на моей машине быстрые тесты показывают, что она часто спит не менее 3 мс, когда я прошу ее спать только 1 мс.

Linux

Наиболее популярным решением, похоже, является nanosleep () API. Однако, если вы хотите < 2 мс спит с высоким разрешением, чем вам нужно использовать вызов sched_setscheduler, чтобы установить поток / процесс для планирования в реальном времени. Если вы неt nanosleep () действует так же, как устаревший usleep, который имел разрешение ~ 10 мс. Другая возможность заключается в использованиисигналы тревоги.

Windows

Решение здесь состоит в том, чтобы использовать мультимедийные времена, как предлагали другие. Если вы хотите подражать Linux 's nanosleep () в Windows, ниже как (оригинал). Опять же, обратите внимание, что вы нене нужно делать CreateWaitableTimer () снова и снова, если вы вызываете sleep () в цикле.

#include <windows.h>    /* WinAPI */

/* Windows sleep in 100ns units */
BOOLEAN nanosleep(LONGLONG ns){
    /* Declarations */
    HANDLE timer;   /* Timer handle */
    LARGE_INTEGER li;   /* Time defintion */
    /* Create timer */
    if(!(timer = CreateWaitableTimer(NULL, TRUE, NULL)))
        return FALSE;
    /* Set timer properties */
    li.QuadPart = -ns;
    if(!SetWaitableTimer(timer, &li, 0, NULL, NULL, FALSE)){
        CloseHandle(timer);
        return FALSE;
    }
    /* Start & wait for timer */
    WaitForSingleObject(timer, INFINITE);
    /* Clean resources */
    CloseHandle(timer);
    /* Slept without problems */
    return TRUE;
}
</windows.h>

Кроссплатформенный код

Вот'сtime_util.cc который реализует сон для Linux, Windows и Apple 'с платформы. Однако обратите внимание, что это неУстановите режим реального времени, используя sched_setscheduler, как я упоминал выше, так что если вы хотите использовать для <2ms тогда это 'Что-то, что вам нужно сделать дополнительно. Еще одно усовершенствование, которое вы можете сделать, - это избегать многократного вызова CreateWaitableTimer для версии Windows, если вы вызываете sleep в каком-то цикле. Как это сделать, см.пример здесь.

#include "time_util.h"

#ifdef _WIN32
#  define WIN32_LEAN_AND_MEAN
#  include <windows.h>

#else
#  include <time.h>
#  include <errno.h>

#  ifdef __APPLE__
#    include <mach clock.h="">
#    include <mach mach.h="">
#  endif
#endif // _WIN32

/**********************************=> unix ************************************/
#ifndef _WIN32
void SleepInMs(uint32 ms) {
    struct timespec ts;
    ts.tv_sec = ms / 1000;
    ts.tv_nsec = ms % 1000 * 1000000;

    while (nanosleep(&ts, &ts) == -1 && errno == EINTR);
}

void SleepInUs(uint32 us) {
    struct timespec ts;
    ts.tv_sec = us / 1000000;
    ts.tv_nsec = us % 1000000 * 1000;

    while (nanosleep(&ts, &ts) == -1 && errno == EINTR);
}

#ifndef __APPLE__
uint64 NowInUs() {
    struct timespec now;
    clock_gettime(CLOCK_MONOTONIC, &now);
    return static_cast<uint64>(now.tv_sec) * 1000000 + now.tv_nsec / 1000;
}

#else // mac
uint64 NowInUs() {
    clock_serv_t cs;
    mach_timespec_t ts;

    host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cs);
    clock_get_time(cs, &ts);
    mach_port_deallocate(mach_task_self(), cs);

    return static_cast<uint64>(ts.tv_sec) * 1000000 + ts.tv_nsec / 1000;
}
#endif // __APPLE__
#endif // _WIN32
/************************************ unix <=**********************************/

/**********************************=> win *************************************/
#ifdef _WIN32
void SleepInMs(uint32 ms) {
    ::Sleep(ms);
}

void SleepInUs(uint32 us) {
    ::LARGE_INTEGER ft;
    ft.QuadPart = -static_cast<int64>(us * 10);  // '-' using relative time

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

static inline uint64 GetPerfFrequency() {
    ::LARGE_INTEGER freq;
    ::QueryPerformanceFrequency(&freq);
    return freq.QuadPart;
}

static inline uint64 PerfFrequency() {
    static uint64 xFreq = GetPerfFrequency();
    return xFreq;
}

static inline uint64 PerfCounter() {
    ::LARGE_INTEGER counter;
    ::QueryPerformanceCounter(&counter);
    return counter.QuadPart;
}

uint64 NowInUs() {
    return static_cast<uint64>(
        static_cast<double>(PerfCounter()) * 1000000 / PerfFrequency());
}
#endif // _WIN32
</double></uint64></int64></uint64></uint64></mach></mach></errno.h></time.h></windows.h>

Еще один более полный кроссплатформенный код может бытьнашел здесь.

Еще одно быстрое решение

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

typedef std::chrono::high_resolution_clock clock;
template <typename t="">
using duration = std::chrono::duration<t>;

static void sleep_for(double dt)
{
    static constexpr duration<double> MinSleepDuration(0);
    clock::time_point start = clock::now();
    while (duration<double>(clock::now() - start).count() < dt) {
        std::this_thread::sleep_for(MinSleepDuration);
    }
}
</double></double></t></typename>

Смежные вопросы

Как заставить поток спать менее чем за миллисекунду в WindowsКроссплатформенная функция Sleep для C ++Есть ли альтернативная функция сна в C до миллисекунд?

Похожие вопросы