Как заставить поток спать менее чем за миллисекунду в Windows - PullRequest
51 голосов
/ 17 сентября 2008

В Windows у меня есть проблема, с которой я никогда не сталкивался в Unix. Вот так можно заставить поток спать менее одной миллисекунды. В Unix у вас обычно есть несколько вариантов (sleep, usleep и nanosleep), чтобы соответствовать вашим потребностям. В Windows, однако, есть только Sleep с точностью до миллисекунды.

В 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?

Ответы [ 18 ]

2 голосов
/ 14 мая 2009

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

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

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

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

1 голос
/ 03 июля 2015

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

1 голос
/ 11 мая 2012

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

Ядро Tickless существует, чтобы помочь улучшить жизнь ноутбуков: c.f. Утилита powertop от Intel.

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

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

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

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

0 голосов
/ 17 сентября 2008

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

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

0 голосов
/ 31 октября 2015

Функция сна намного меньше миллисекунды, может быть

Я обнаружил, что сон (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

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

0 голосов
/ 18 сентября 2008

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

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

0 голосов
/ 23 апреля 2017

Если ваша цель состоит в том, чтобы «подождать очень короткое время» , потому что вы выполняете spinwait , тогда вы можете увеличить уровни ожидания, которые вы можете выполнить.

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);
}

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

0 голосов
/ 17 сентября 2008

В Windows использование select заставляет вас включить библиотеку Winsock , которая должна быть инициализирована следующим образом:

WORD wVersionRequested = MAKEWORD(1,0);
WSADATA wsaData;
WSAStartup(wVersionRequested, &wsaData);

И тогда select не позволит вам вызываться без какого-либо сокета, поэтому вам придется сделать немного больше для создания метода микросна:

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 возвращают ноль в случае успеха и ненулевое значение для ошибок.

...