Все, что я прочитал как на сайте MS docs (где он на самом деле не адресован), так и здесь, в SO, говорит о том, что Windows WaitForSingleObject()
не подвержена ложным пробуждениям и ожидает как минимум предоставленное время, и, возможно,дольше.Тем не менее, мое тестирование говорит, что это не так, и на самом деле ранние пробуждения случаются почти всегда.Является ли «здравый смысл» неправильным, и мне просто нужно добавить циклы для обработки ранних пробуждений, или я делаю что-то не так, и мне нужно постоянно биться головой об этом, чтобы попытаться выяснить?
К сожалению, полныйкод слишком сложен для размещения здесь, но у меня есть две разные темы, каждая со своим собственным событием, созданным с помощью:
event = CreateEventA(NULL, false, false, NULL);
(событие является локальной переменной потока).У меня есть мьютекс, который я использую для обеспечения того, чтобы оба потока запускались примерно в одно и то же время.
В каждом потоке я вызываю WaitForSingleObject()
.В этом конкретном тесте я никогда не вызываю SetEvent()
, поэтому единственный способ закончить это через тайм-аут, и код возврата показывает, что это происходит.Однако фактическое количество времени, затрачиваемое на ожидание, сильно варьируется, и 90% времени на меньше , чем запрошенное мной время.Я инструктировал это, используя QueryPerformanceCounter()
, чтобы определить, сколько времени здесь проведено, и это просто неправильно.Вот инструментальный код:
LARGE_INTEGER freq, ctr1, ctr2;
QueryPerformanceFrequency(&freq);
QueryPerformanceCounter(&ctr1);
DWORD ret = WaitForSingleObject(event, tmoutMs);
QueryPerformanceCounter(&ctr2);
uint64_t elapsed = ((uint64_t)ctr2.QuadPart - (uint64_t)ctr1.QuadPart) * 1000000ULL / (uint64_t)freq.QuadPart;
(здесь elapsed
хранится в микросекундах, просто чтобы быть более конкретным) Затем я распечатываю эту информацию.В одном потоке tmoutMs
равен 2, а в другом потоке tmoutMs
равен 100. Почти каждый раз, когда возвращаемые значения слишком короткие: ожидание 2 мс может занять от 700 мкс, а ожидание 100 мс - от 93 мс.Только один раз в 7 попыток или около того истекшее время будет> 100 мс.Вот некоторые примеры выходных данных:
event=104: pause(tmoutMs=2) => ret=258 elapsed us=169, ms=0
event=112: pause(tmoutMs=100) => ret=258 elapsed us=93085, ms=93
event=104: pause(tmoutMs=2) => ret=258 elapsed us=427, ms=0
event=112: pause(tmoutMs=100) => ret=258 elapsed us=94002, ms=94
event=104: pause(tmoutMs=2) => ret=258 elapsed us=3317, ms=3
event=112: pause(tmoutMs=100) => ret=258 elapsed us=96840, ms=96
event=104: pause(tmoutMs=2) => ret=258 elapsed us=11461, ms=11
event=112: pause(tmoutMs=100) => ret=258 elapsed us=105189, ms=105
Код возврата всегда WAIT_TIMEOUT, как и ожидалось.
Это разумно, даже если это не задокументировано (или задокументировано где-то, что я просто могу ')не могу найти), и мне просто нужно сделать цикл самостоятельно для обработки ранних пробуждений?
FWIW, это программа на C ++, скомпилированная с Visual Studio 2017, работающая на Windows10.Это программа модульного тестирования, использующая Google Test, и не имеет графического интерфейса: это только командная строка.