В Windows у меня есть проблема, с которой я никогда не сталкивался в Unix. Вот как перевести поток в спящий режим менее чем на одну миллисекунду. В Unix у вас обычно есть несколько вариантов (сон, usleep и nanosleep) в соответствии с вашими потребностями. В 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?
Это связано с тем, что большинство компьютеров, на которых работает Windows, имеют аппаратные ограничения в диапазоне 1-10 мс. Компьютерное оборудование для ПК стоит дешево. Вам понадобится специализированное оборудование, чтобы следить за точным временем. Например, карты Wi-Fi: время передачи маяка в миллисекундах должно оставаться аппаратным (даже в Linux) из-за ненадежности хронометража ПК.
nanosleep().
Возможный дубликат Требуется точный сон потока. Ошибка макс. 1 мс





В Windows использование select вынуждает вас включить библиотеку Winsock, которая должна быть инициализирована в вашем приложении следующим образом:
WORD wVersionRequested = MAKEWORD(1,0);
WSADATA wsaData;
WSAStartup(wVersionRequested, &wsaData);
И тогда выбор не позволит вам вызываться без какого-либо сокета, поэтому вам нужно сделать немного больше, чтобы создать метод микросна:
int usleep(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/1000000L;
tv.tv_usec = usec%1000000L;
return select(0, 0, 0, &dummy, &tv);
}
Все эти созданные методы usleep возвращают ноль в случае успеха и ненулевое значение в случае ошибок.
Эта имплантация Sleep () пропускает сокет каждый раз, когда он вызывается!
Этот метод «спит» около 15 миллисекунд, даже если запрошен только 1 микросекундный спящий режим.
Вам не нужно создавать сокет для вызова select (). Пожалуйста, удалите этот ответ.
Это копирует реализацию, но не семантику. Вы не можете реализовать задержку с продолжительностью меньше, чем частота таймера. Извините, это бесполезно и вводит в заблуждение. По крайней мере, отмените выбор этого ответа как принятого. Конечно, поскольку вы никогда не решались задать вопрос, любой ответ был бы одинаково хорош, если бы он никогда не вводил в заблуждение.
Это указывает на неправильное понимание функций сна. Передаваемый вами параметр - это время сна минимум. Нет никакой гарантии, что поток проснется точно в указанное время. Фактически, потоки вообще не «просыпаются», а выбираются для выполнения планировщиком ОС. Планировщик может выбрать ожидание намного дольше, чем запрошенная продолжительность сна, чтобы активировать поток, особенно если другой поток все еще активен в этот момент.
Да, Sleep () означает просто подсказку. Согласно MSDN время Sleep () на самом деле может быть меньше, чем то, что вы запрашиваете. Это просто руководство для ОС по повышению производительности, а не совсем хороший механизм синхронизации при степени детализации любой.
Однако гарантию можно получить при тщательной реализации. Правильная установка приоритетов потоков / процессов и сходства процессоров обсуждается здесь.
Здесь есть серьезная дезинформация: «Передаваемый вами параметр - минимальное время сна». Не актуально для окон: msdn.microsoft.com/en-gb/library/windows/desktop/…Если dwMilliseconds меньше разрешения системных часов, поток может спать меньше указанного промежутка времени. Если dwMilliseconds больше одного тика, но меньше двух, ожидание может составлять от одного до двух тиков и так далее.
Отличный ответ на несуществующий вопрос. Несмотря на то, что все ответы ни на один вопрос верны, этот действительно хорош.
Это просто не отвечает на вопрос. Это хороший ответ на другой вопрос, поэтому я очень не хочу давать вам голос против, но слишком многие ошибочно проголосовали за вас. Если бы вы сказали «не можете, и вот почему», вы бы ответили на вопрос.
Как говорит Джоэл, вы не можете осмысленно «спать» (то есть отказываться от запланированного CPU) в течение таких коротких периодов времени. Если вы хотите отложить на короткое время, вам нужно вращаться, неоднократно проверяя таймер с подходящим высоким разрешением (например, «таймер производительности») и надеясь, что что-то с высоким приоритетом все равно вас не упустит.
Если вы действительно заботитесь о точных задержках в такие короткие промежутки времени, вам не следует использовать Windows.
-1 за то, что не учитывает, что пользователь приложения может иметь другие предпочтения, чем разработчики.
@AustinMullins Не могли бы вы немного рассказать об этом? Вы искали какое-нибудь заявление о пороках занятого прядения?
Нет, я имел в виду замечание «вам не следует использовать Windows». Разработчик должен делать программы для пользователя, который, вероятно, хочет, чтобы программа работала в Windows.
А ну понятно. Что ж, если разработчик или его пользователи предвидят требование жесткого реального времени, как описано в вопросе, тогда Windows не та ОС, которую им следует пытаться использовать. Это не уничижительный комментарий к Windows и не лицензия на высокомерие разработчиков, это просто мнение о технической пригодности, которое (как разработчик Windows :-) я рад поддержать.
Я один из разработчиков, использующих окна принужденный, потому что нашему приложению необходимо взаимодействовать с другим, доступным также в Windows. После моих тестов я согласен с этим решением, которое дало нам наилучшие результаты, но я категорически не согласен с вашим последним предложением «вам не следует использовать Windows»: вы не можете знать, почему разработчику нужно спать для суб- Интервал мс. Про этот здесь есть даже целый проект
@gog - Вы не можете надежно засыпать (или задерживать любым другим способом) на такое короткое время в Windows (кроме, возможно, написания драйвера режима ядра). Неважно, какие еще коммерческие или прагматические требования делают Windows хорошим выбором, она просто не предназначена для такого рода вещей. Как я неоднократно повторял за 10 лет, прошедших с тех пор, как написал первоначальный ответ, это не критика, это наблюдение. Ваш жесткий цикл может большую часть времени ждать нужного времени, но иногда он будет ждать дольше, и если вы не можете этого допустить, Когда-либо не сможет использовать Windows.
@WillDean Но, используя ваше решение (QueryPerformanceCounter), я смог спать в течение микросекунд в Windows, так же, как это доступно в Unix. Это исходный вопрос. Вопрос не касается RTOS, где приложение никогда не может выдерживать более длительный сон. Даже Sleep () не может этого гарантировать. И даже ядро Linux в стандартных дистрибутивах не гарантирует этого. Я думаю, что последнее предложение не относится к этому ответу, даже если я проголосовал за него.
Используйте мультимедийные таймеры высокого разрешения, доступные в winmm.lib. См. Пример это.
Я удивлен, что за это не проголосовали больше. Это реальное решение. Почему все голосуют за «Сон - это всего лишь предположение» и «Прекратите использовать Windows, переключитесь на Unix»? Я признаю, что полезно понимать, что сон - это всего лишь предположение, но это не решение. И не у всех есть возможность выйти из Windows. Чтобы быть ясным, это решение дает вам точность только 1 мс (с использованием библиотеки мультимедиа). Это то что мне нужно. Я не пробовал технику «выбора» (опубликованную Хендриком и Сминком), но похоже, что она может снизить точность до субмиллисекунд.
Связанный код @GabeHalsmer может ждать только 1 или более мсек, это не то, о чем спрашивают. Более того, он не компилируется из коробки, содержит ошибки и не является потокобезопасным и может либо вызывать тупиковые ситуации, либо вообще не ждать. Даже после исправления некоторых проблем, чтобы заставить его делать то, что он должен делать, и просить его подождать 1 мс, я видел, как он ждал где-то от 1 до 3 мс, и это были просто быстрые тесты в системе без большой нагрузки. Таким образом, он не работает лучше, а хуже, чем более простая комбинация WaitableTimer / WaitForSingleObject путь, которая, по крайней мере, выполняет ожидания sub-mSec. Итак: не совсем подходящее решение для всех.
Все комментарии, относящиеся к Linux, должны быть помечены и удалены. @Gabe, вы правы, пользователи ОС с рыночной долей в 1% часто пытаются шуметь.
Что вы ждете, что требует такой точности? В общем, если вы необходимость для указания этого уровня точности (например, из-за зависимости от некоторого внешнего оборудования), вы находитесь на неправильной платформе и должны смотреть на ОС реального времени.
В противном случае вам следует подумать, есть ли событие, с которым вы можете синхронизироваться, или, в худшем случае, просто занято, дождитесь процессора и используйте API счетчика высокой производительности для измерения прошедшего времени.
Да, вам нужно понимать кванты времени вашей ОС. В Windows вы даже не получите время разрешения 1 мс, если не измените квант времени на 1 мс. (Используя, например, timeBeginPeriod () / timeEndPeriod ()) Это все равно ничего не гарантирует. Даже небольшая загрузка или один дрянной драйвер устройства все скинут.
SetThreadPriority () помогает, но довольно опасно. Плохие драйверы устройств могут вас испортить.
Вам нужна сверхуправляемая вычислительная среда, чтобы эта уродливая штука вообще работала.
Вы просто должны быть осторожны, чтобы не зацикливаться, иначе другие процессы будут голодны ...
Я в основном говорил о других вещах, которые вы здесь не контролируете. Довольно легко получить что-то, что работает на одном компьютере, полностью выйдет из строя на другом, потому что плохой драйвер устройства держится слишком долго. Если вы разрабатываете что-то для внутреннего использования, это нормально ... если вам нужно что-то, что вы можете выпустить в мир, это действительно болезненно.
Попробуйте boost :: xtime и timed_wait ()
имеет наносекундную точность.
Вы путаете точность с точностью.
Как указали несколько человек, сон и другие связанные функции по умолчанию зависят от «системного тика». Это минимальная единица времени между задачами ОС; например, планировщик не будет работать быстрее этого. Даже в ОС реального времени системный тик обычно составляет не менее 1 мс. Хотя он настраивается, это имеет последствия для всей системы, а не только для вашей функции сна, потому что ваш планировщик будет работать чаще и потенциально увеличивает накладные расходы вашей ОС (количество времени для запуска планировщика по сравнению с количеством время выполнения задачи).
Решением этого является использование внешнего высокоскоростного тактового устройства. Большинство систем Unix позволяют вам указывать свои таймеры и такие разные часы для использования, в отличие от системных часов по умолчанию.
Если вам нужна такая степень детализации, вы находитесь не в том месте (в пространстве пользователя).
Помните, что если вы находитесь в пользовательском пространстве, ваше время не всегда точно.
Планировщик может запускать ваш поток (или приложение) и планировать его, поэтому вы зависите от планировщика ОС.
Если вы ищете что-то точное, вам нужно пойти: 1) В пространстве ядра (например, в драйверах) 2) Выберите RTOS.
В любом случае, если вы ищете некоторую детализацию (но помните о проблеме с пользовательским пространством), посмотрите Функция QueryPerformanceCounter и функция QueryPerformanceFrequency в MSDN.
Просто используйте Sleep (0). 0 явно меньше миллисекунды. Звучит смешно, но я серьезно. Sleep (0) сообщает Windows, что вам сейчас нечего делать, но вы действительно хотите, чтобы вас пересмотрели, как только планировщик снова запустится. И поскольку очевидно, что выполнение потока нельзя запланировать до запуска самого планировщика, это самая короткая возможная задержка.
Обратите внимание, что вы можете передать в usleep число в микросекундах, но это также означает void usleep (__ int64 t) {Sleep (t / 1000); } - нет никаких гарантий, что вы действительно спите в этот период.
Я думаю, что если вы действительно попробуете это, вы обнаружите, что Sleep (0) обычно спит в течение 10-15 мс в зависимости от вашего оборудования и загрузки системы.
Из MSDN: нулевое значение заставляет поток передать оставшуюся часть своего временного интервала любому другому потоку с таким же приоритетом, который готов к запуску. Если нет других потоков с равным приоритетом, готовых к запуску, функция немедленно возвращается, и поток продолжает выполнение.
Последняя часть ответа неверна. Если вы разделите int на 1000, тогда любое значение ниже 1000 будет установлено в 0. Таким образом, ваша функция usleep просто вызовет Sleep (0) для любого значения меньше миллисекунды. И, как сказал @Ferruccio, Sleep (0) не спит меньше миллисекунды, здесь это опасность.
У меня такая же проблема, и ничто не работает быстрее, чем мс, даже Sleep (0). Моя проблема заключается в связи между клиентским и серверным приложением, где я использую функцию _InterlockedExchange для тестирования и установки бита, а затем я сплю (0).
Мне действительно нужно выполнять тысячи операций в секунду таким образом, и он работает не так быстро, как я планировал.
Поскольку у меня есть тонкий клиент, работающий с пользователем, который, в свою очередь, вызывает агента, который затем разговаривает с потоком, я скоро перейду к объединению потока с агентом, чтобы не требовалось никакого интерфейса событий.
Чтобы вы, ребята, поняли, насколько медленным является этот сон, я провел тест в течение 10 секунд, выполняя пустой цикл (получилось что-то вроде 18 000 000 циклов), тогда как при наличии события у меня было только 180 000 циклов. То есть в 100 раз медленнее!
Меня не удивляет. Поскольку пустой цикл будет выполняться во внутреннем кэше процессора, когда он пуст. Меня не удивит, если компилятор даже оптимизирует цикл и просто выдаст вам результат. Лучший тест, который на самом деле что-то делает в обоих случаях, и сравнивает результат. попробуйте выполнить _InterlockedIncrement (внутренний) при каждом взаимодействии обоих циклов.
Фактически использование этой функции 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;
}
Вам не нужно создавать сокет только для того, чтобы иметь возможность вызывать select ()
Я исправил образец кода. Имейте в виду, что вы должны вызвать WSAStartup / WSACleanup до / после использования этой функции при кодировании под Windows.
Как уже упоминалось, действительно нет никаких гарантий относительно времени сна. Но никто не хочет признавать, что иногда в простаивающей системе команда usleep может быть очень точной. Особенно с бестикальным ядром. Он есть в Windows Vista, а в Linux - начиная с версии 2.6.16.
Бесконтактные ядра существуют, чтобы помочь улучшить жизнь ноутбуков: ср. Утилита Intel powertop.
В этом состоянии я случайно измерил команду Linux usleep, которая очень точно соблюдает запрошенное время сна, до полдюжины микросекунд.
Так что, возможно, OP хочет что-то, что будет грубо работать большую часть времени в системе на холостом ходу, и иметь возможность запрашивать планирование микросекунд! На самом деле я бы тоже хотел этого в Windows.
Также Sleep (0) звучит как boost :: thread :: yield (), что терминология более ясна.
Интересно, имеют ли блокировки с синхронизацией Способствовать росту более высокую точность. Потому что тогда вы можете просто заблокировать мьютекс, который никто никогда не выпускает, а когда истечет время ожидания, продолжить ... Таймауты устанавливаются с помощью boost :: system_time + boost :: milliseconds & cie (xtime не рекомендуется).
Обычно сон длится, по крайней мере, до следующего системного прерывания. Однако это
зависит от настроек ресурсов мультимедийного таймера. Это может быть что-то близкое к
1 мс, некоторое оборудование даже позволяет работать с периодами прерывания 0,9765625 (Фактическое разрешение, предоставленный NtQueryTimerResolution, покажет 0,9766, но это на самом деле неверно. Они просто не могут поместить правильное число в формат Фактическое разрешение. Это 0,9765625 мс при 1024 прерываниях в секунду) .
Есть одно исключение, которое позволяет нам избежать того факта, что может быть невозможно спать меньше периода прерывания: это знаменитый Sleep(0). Это очень мощный
инструмент и используется не так часто, как нужно! Он отказывается от напоминания о временном интервале потока. Таким образом, поток остановится до тех пор, пока планировщик не заставит поток снова получить службу процессора. Sleep(0) - асинхронная служба, вызов заставит планировщик реагировать независимо от прерывания.
Второй способ - использование waitable object. Функция ожидания, такая как WaitForSingleObject(), может ждать события. Чтобы поток был спящим в течение любого времени, в том числе в микросекундном режиме, потоку необходимо настроить некоторый служебный поток, который будет генерировать событие с желаемой задержкой. «Спящий» поток настроит этот поток, а затем остановится в функции ожидания, пока служебный поток не установит сигнальное событие.
Таким образом, любой поток может «спать» или ждать в любое время. Сервисный поток может быть очень сложным и предлагать общесистемные сервисы, такие как синхронизированные события с микросекундным разрешением. Однако разрешение в микросекундах может вынудить поток службы вращаться на службе времени с высоким разрешением не более одного периода прерывания (~ 1 мс). Если соблюдать осторожность, это может работают очень хорошо, особенно на многопроцессорных или многоядерных системах. Вращение в одну мс не сильно повредит в многоядерной системе, когда маска сродства для вызывающего потока и потока службы тщательно обрабатывается.
Код, описание и тестирование можно найти на Проект отметки времени Windows
Попробуйте использовать SetWaitableTimer ...
#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);
}
Очень хорошо подходит для очень короткого сна. Однако помните, что в определенный момент фактические задержки никогда не будут согласованными, потому что система не может поддерживать согласованные задержки в такое короткое время.
Интересный факт: timeBeginPeriod / timeEndPeriod внутренне использует ZwSetTimerResolution, и еще один интересный факт - минимальное временное разрешение составляет 0,5 мс, с timeBeginPeriod вы получите минимум 1 мс, но с ZwSetTimerResolution вы можете получить 0,5 мс, поэтому вызов ZwSetTimerResolution с 1 эквивалентен его вызову. с 5000 и ниже. (Это в блоке 100 нс, или ограничении 10 МГц)
Функция сна, которая длится меньше миллисекунды - возможно
Я обнаружил, что у меня работает sleep (0). В системе с почти 0% загрузкой процессора в диспетчере задач я написал простую консольную программу, и функция sleep (0) спала в течение последовательных 1-3 микросекунд, что намного меньше миллисекунды.
Но из приведенных выше ответов в этом потоке я знаю, что количество спящих сна (0) может варьироваться гораздо более дико, чем это в системах с большой загрузкой процессора.
Но как я понимаю, функцию сна не стоит использовать как таймер. Его следует использовать для того, чтобы программа использовала как можно меньше ресурсов процессора и выполнялась как можно чаще. Я думаю, что для моих целей, таких как перемещение снаряда по экрану в видеоигре, намного быстрее, чем один пиксель в миллисекунду, подойдет sleep (0).
Вы просто должны убедиться, что интервал сна намного меньше, чем наибольшее время, в течение которого он будет спать. Вы не используете спящий режим в качестве таймера, а просто для того, чтобы игра использовала минимально возможный процент процессора. Вы бы использовали отдельную функцию, которая не имеет ничего общего, это сон, чтобы узнать, когда прошло определенное количество времени, а затем переместить снаряд на один пиксель по экрану, скажем, за 1/10 миллисекунды или 100 микросекунд. .
Псевдокод будет выглядеть примерно так.
while (timer1 < 100 microseconds) {
sleep(0);
}
if (timer2 >=100 microseconds) {
move projectile one pixel
}
//Rest of code in iteration here
Я знаю, что ответ может не работать для сложных задач или программ, но может работать для некоторых или многих программ.
В играх вы не должны полагаться на тайминги кадров, вместо этого используйте скорость * тайминг. Таким образом, у вас не будет проблем, если компьютер будет перегружен.
@CemKalyoncu = или если тот же код будет запущен намного позже на значительно лучшем оборудовании. Я написал несколько старых рото-зумеров, систем частиц и различных эффектов для 386 и 486. Я уверен, вы можете представить, насколько непристойными они были при запуске на i3 (в одном случае от 20 кадров в секунду до более 700! !!)
Если ваша цель - "подождать очень короткое время", потому что вы выполняете спинвейт, то уровни ожидания, которые вы можете выполнить, увеличиваются.
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);
}
Достоинство здесь в том, что мы каждый раз ждем дольше и в конечном итоге полностью засыпаем.
Это не работает в Windows. Минимальное время "сна" при выборе по-прежнему составляет около 1 мс (Vista, поток RT, timeBeginPeriod (1), MMCSS "Pro Audio" RT Critical).