Подтвердить что ты не робот

Как сделать поток сна менее миллисекунды в Windows

В Windows у меня проблема, с которой я никогда не сталкивался в Unix. Вот как получить поток спать менее чем за миллисекунду. В Unix у вас обычно есть выбор (сон, спать и наносить), чтобы он соответствовал вашим потребностям. Однако в Windows есть только сон с миллисекундной детализацией.

В Unix я могу использовать системный вызов select для создания микросекундного сна, который довольно прост:

int usleep(long usec)
{
    struct timeval tv;
    tv.tv_sec = usec/1000000L;
    tv.tv_usec = usec%1000000L;
    return select(0, 0, 0, 0, &tv);
}

Как я могу добиться того же самого в Windows?

4b9b3361

Ответ 1

В Windows использование select заставляет вас включить библиотеку

Ответ 2

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

Ответ 3

Как говорит Джоэл, вы не можете осмысленно "спать" (т.е. отказываться от запланированного процессора) за такие короткие периоды. Если вы хотите задерживаться на некоторое короткое время, вам нужно откручивать, повторно проверять подходящий таймер с высоким разрешением (например, "таймер производительности" ) и надеяться, что что-то с высоким приоритетом не уберет вас в любом случае.

Если вам действительно нужны точные задержки в такие короткие времена, вы не должны использовать Windows.

Ответ 4

Используйте таймеры с высоким разрешением, доступные в winmm.lib. См. этот для примера.

Ответ 5

Да, вам нужно понять кванты времени вашей ОС. В Windows вы даже не получите разрешение 1 мс, если вы не измените квант времени на 1 мс. (Использование, например, timeBeginPeriod()/timeEndPeriod()) Это все равно ничего не гарантирует. Даже небольшая нагрузка или один дерьмовый драйвер устройства отбросят все.

SetThreadPriority() помогает, но довольно опасен. Плохие драйверы устройств могут погубить вас.

Вам нужна ультра-контролируемая вычислительная среда, чтобы сделать этот уродливый материал вообще.

Ответ 6

Если вы хотите настолько гранулярности, что вы находитесь не в том месте (в пространстве пользователя).

Помните, что если вы находитесь в пространстве пользователя, ваше время не всегда точное.

Планировщик может запустить ваш поток (или приложение) и запланировать его, поэтому вы зависите от планировщика ОС.

Если вы ищете что-то точное, вам нужно идти: 1) В пространстве ядра (например, драйверах) 2) Выберите RTOS.

В любом случае, если вы ищете некоторую детализацию (но помните о проблеме с пользовательским пространством), посмотрите Функция QueryPerformanceCounter и функция QueryPerformanceFrequency в MSDN.

Ответ 7

Как отметили несколько человек, сон и другие связанные функции по умолчанию зависят от "системного тика". Это минимальная единица времени между задачами ОС; планировщик, например, не будет работать быстрее, чем это. Даже с ОС реального времени, системный тик обычно не меньше 1 мс. Хотя это настраивается, это имеет последствия для всей системы, а не только для вашей функциональности сна, поскольку ваш планировщик будет работать чаще и потенциально увеличивать накладные расходы вашей ОС (количество времени для запуска планировщика, а также количество время выполнения задачи).

Решением этого является использование внешнего высокоскоростного устройства синхронизации. Большинство систем Unix позволят вам указать ваши таймеры и такие разные часы для использования, в отличие от системных часов по умолчанию.

Ответ 8

Чего вы ждете, для чего нужна такая точность? В общем случае, если вы нуждаетесь, чтобы указать этот уровень точности (например, из-за зависимости от какого-либо внешнего оборудования), вы находитесь на неправильной платформе и должны смотреть на ОС реального времени.

В противном случае вам следует подумать о том, есть ли событие, которое вы можете синхронизировать, или в худшем случае просто ожидание CPU и использование высокопроизводительного счетчика API для измерения прошедшего времени.

Ответ 9

Обычно сон будет продолжаться, по крайней мере, до тех пор, пока не произойдет следующее прерывание системы. Однако это зависит от настроек ресурсов мультимедийного таймера. Может быть установлено что-то близкое к 1 мс, некоторые аппаратные средства даже позволяют работать в периоды прерываний 0.9765625 (ActualResolution, предоставленный NtQueryTimerResolution, покажет 0.9766, но это действительно неправильно. Они просто не могут поместить правильный номер в формат ActualResolution. Он равен 0,9765625мс при 1024 прерываниях на второй).

Есть одно исключение, которое позволяет нам избежать из-за того, что спать может быть невозможно меньше, чем период прерывания: это знаменитый Sleep(0). Это очень мощный инструмент, и он не используется так часто, как нужно! Он отказывается от напоминания об срезе времени потока. Таким образом, поток остановится до тех пор, пока планировщик не заставляет поток снова получать службу cpu. Sleep(0) - это асинхронная служба, вызов заставит планировщика реагировать независимо от прерывания.

Второй способ - использование waitable object. Функция ожидания, например WaitForSingleObject(), может ждать события. Для того, чтобы нить спала в любое время, а также раз в микросекундном режиме, потоку необходимо настроить некоторую служебную нить, которая будет генерировать событие с требуемой задержкой. "Спящий" поток установит этот поток, а затем приостановит функцию ожидания до тех пор, пока поток службы не установит событие.

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

Код, описание и тестирование можно посетить в Project Timestamp в Windows

Ответ 10

У меня такая же проблема, и ничто не кажется быстрее, чем ms, даже Sleep (0). Моя проблема заключается в связи между клиентом и серверным приложением, в котором я использую функцию _InterlockedExchange для проверки и установки бит, а затем спящего режима (0).

Мне действительно нужно выполнять тысячи операций в секунду таким образом, и это не работает так быстро, как я планировал.

Поскольку у меня есть тонкий клиент, взаимодействующий с пользователем, который, в свою очередь, вызывает агента, который затем говорит с потоком, я скоро перейду, чтобы объединить поток с агентом, чтобы интерфейс событий не требовался.

Просто, чтобы дать вам ребят идею о том, как медленно этот сон, я провел тест на 10 секунд, выполняя пустую петлю (получая что-то вроде 18 000 000 циклов), тогда как с событием на месте я получил только 180 000 циклов. То есть, в 100 раз медленнее!

Ответ 11

Фактически использование этой функции usleep приведет к большой утечке памяти/ресурса. (в зависимости от того, как часто называют)

используйте эту исправленную версию (извините, не можете отредактировать?)

bool usleep(unsigned long usec)
{
    struct timeval tv;
    fd_set dummy;
    SOCKET s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    FD_ZERO(&dummy);
    FD_SET(s, &dummy);
    tv.tv_sec = usec / 1000000ul;
    tv.tv_usec = usec % 1000000ul;
    bool success = (0 == select(0, 0, 0, &dummy, &tv));
    closesocket(s);
    return success;
}

Ответ 12

#include <Windows.h>

static NTSTATUS(__stdcall *NtDelayExecution)(BOOL Alertable, PLARGE_INTEGER DelayInterval) = (NTSTATUS(__stdcall*)(BOOL, PLARGE_INTEGER)) GetProcAddress(GetModuleHandle("ntdll.dll"), "NtDelayExecution");

static NTSTATUS(__stdcall *ZwSetTimerResolution)(IN ULONG RequestedResolution, IN BOOLEAN Set, OUT PULONG ActualResolution) = (NTSTATUS(__stdcall*)(ULONG, BOOLEAN, PULONG)) GetProcAddress(GetModuleHandle("ntdll.dll"), "ZwSetTimerResolution");




static void SleepShort(float milliseconds) {
    static bool once = true;
    if (once) {
        ULONG actualResolution;
        ZwSetTimerResolution(1, true, &actualResolution);
        once = false;
    }

    LARGE_INTEGER interval;
    interval.QuadPart = -1 * (int)(milliseconds * 10000.0f);
    NtDelayExecution(false, &interval);
}

да, он использует некоторые недокументированные функции ядра, но работает очень хорошо, я использую SleepShort (0.5); в некоторых моих threds

Ответ 13

Как и все упомянутые, действительно нет гарантий относительно времени сна. Но никто не хочет признавать, что иногда, на незанятой системе, команда usleep может быть очень точной. Особенно с безрадовым ядром. Windows Vista имеет это, и Linux имеет это начиная с 2.6.16.

Тиккерные ядра существуют, чтобы помочь улучшить работоспособность ноутбуков: c.f. Утилита Intel PowerTop.

В этом состоянии я, вероятно, измерил команду Linux usleep, которая очень внимательно следила за запрошенным временем сна, вплоть до полутора десятков микросекунд.

Итак, возможно, OP хочет что-то, что будет работать большую часть времени в системе на холостом ходу и сможет запросить микросекундное планирование! Я бы тоже хотел это сделать в Windows.

Также Sleep (0) звучит как boost:: thread:: yield(), эта терминология понятна.

Интересно, имеет ли Boost -timed блокировки лучшую точность. Потому что тогда вы можете просто заблокировать мьютекс, который никто никогда не выпускает, и когда тайм-аут достигнут, продолжайте... Тайм-ауты устанавливаются с помощью boost:: system_time + boost:: milliseconds и cie (время устаревания устарело).

Ответ 14

Попробуйте boost:: xtime и timed_wait()

имеет наносекундную точность.

Ответ 15

Просто используйте Sleep (0). 0, очевидно, меньше миллисекунды. Это звучит смешно, но я серьезно. Sleep (0) сообщает Windows, что вам сейчас нечего делать, но вы хотите, чтобы вас пересмотрели, как только планировщик снова запустится. И так как очевидно, что поток не может быть запланирован для запуска до запуска самого планировщика, это самая короткая возможная задержка.

Обратите внимание, что вы можете передать номер микросекунды вашему спящему значению, но также не действует (__ int64 t) {Sleep (t/1000); } - никаких гарантий фактически спать в этот период.

Ответ 16

Попробуйте использовать SetWaitableTimer...

Ответ 17

Функция сна, которая меньше, чем миллисекунда, возможно

Я обнаружил, что сон (0) работал на меня. В системе с нагрузкой почти 0% на CPU в диспетчере задач я написал простую консольную программу, а функция sleep (0) спала в течение последовательных 1-3 микросекунд, что меньше, чем миллисекунда.

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

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

Вы просто убедитесь, что интервал ожидания меньше, чем наибольшее время, в которое он будет спать. Вы не используете сон в качестве таймера, но только для того, чтобы игра использовала минимальное количество процентного процента процессора. Вы использовали бы отдельную функцию, которая не имеет ничего общего, это сон, чтобы узнать, когда прошло определенное количество времени, а затем переместить один пиксель по экрану на экране - в момент времени 1/10 с миллисекунды или 100 микросекунд.

Псевдокод будет выглядеть примерно так.

while (timer1 < 100 microseconds) {
sleep(0);
}

if (timer2 >=100 microseconds) {
move projectile one pixel
}

//Rest of code in iteration here

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

Ответ 18

Если ваша цель - "дождаться очень короткого промежутка времени", потому что вы делаете спинвейт, тогда возрастает уровень ожидания, который вы можете выполнить.

void SpinOnce(ref Int32 spin)
{
   /*
      SpinOnce is called each time we need to wait. 
      But the action it takes depends on how many times we've been spinning:

      1..12 spins: spin 2..4096 cycles
      12..32: call SwitchToThread (allow another thread ready to go on time core to execute)
      over 32 spins: Sleep(0) (give up the remainder of our timeslice to any other thread ready to run, also allows APC and I/O callbacks)
   */
   spin += 1;

   if (spin > 32)
      Sleep(0); //give up the remainder of our timeslice
   else if (spin > 12)
      SwitchTothread(); //allow another thread on our CPU to have the remainder of our timeslice
   else
   {
      int loops = (1 << spin); //1..12 ==> 2..4096
      while (loops > 0)
         loops -= 1;
   }
}

Итак, если ваша цель состоит в том, чтобы просто подождать немного, вы можете использовать что-то вроде:

int spin = 0;
while (!TryAcquireLock()) 
{ 
   SpinOne(ref spin);
}

Достоинство здесь заключается в том, что мы ждем дольше каждый раз, в конечном итоге полностью спать.