Таймер ожидания не ожидает указанного периода времени - PullRequest
0 голосов
/ 21 февраля 2019

Я создаю ручной таймер ожидания следующим образом

m_hTimer = CreateWaitableTimer(NULL, true, NULL);

И использую его в функции

HRESULT ClassA::InduceSleep(UINT32 uiMiliSeconds)
{
    if (m_hTimer)
    {
        LARGE_INTEGER liDueTime;
        liDueTime.QuadPart = (uiMiliSeconds) * (-10000) * (1LL);
        if (!SetWaitableTimer(m_hTimer, &liDueTime, 0, NULL, NULL, 0))
        {
            Log("SetWaitableTimer failed GLE[%d]", GetLastError());
            goto exit;
        }

        // Wait for the timer.

        if (WaitForSingleObject(m_hTimer, INFINITE) != WAIT_OBJECT_0)
        {
            Log("WaitForSingleObject failed GLE[%d]", GetLastError());
        }
        return S_OK;
    }
exit:    
    Sleep(uiMiliSeconds);
    return S_OK;
}

Я наблюдаю, что при вызове InduceSleep () с задержкой на 10 секунд(или 5 секунд) объект waitforsingle возвращает объект WAIT_OBJECT_0 немедленно, без какой-либо задержки, поэтому таймер немедленно сигнализируется.В документации упоминается, что установленный таймер останавливается и повторно активирует таймер, поэтому он не должен находиться в сигнальном состоянии и должен сигнализироваться только по истечении заданного времени.Что мне здесь не хватает?

1 Ответ

0 голосов
/ 21 февраля 2019

Это простая ошибка:

uiMiliSeconds имеет тип UINT32, т. Е. Тип без знака.

Это делает

(uiMiliSeconds) * (-10000)

умножением без знака, т.е.-10000 преобразуется в unsigned перед умножением.(Согласен, продвижение типов иногда является сложной темой.)

Я пробовал это в минимальном примере:

#include <iostream>

int main()
{
  uint32_t uiMilliseconds = 10 * 1000;
  std::cout << uiMilliseconds * -10000 << '\n';
  std::cout << (int)uiMilliseconds * -10000 << '\n';
  return 0;
}

Вывод:

4194967296
-100000000

Демонстрация в реальном времени на coliru

Итак, решение заключается в простом преобразовании uiMiliseconds в целое число со знаком перед умножением (как я делал во втором выводелиния).

Остальная часть истории, вероятно, очевидна для ОП.Из MSDN о функция SetWaitableTimer :

lpDueTime

Время, по истечении которого состояние таймера должно быть установлено в виде сигнала, с интервалом в 100 наносекунд,Используйте формат, описанный структурой FILETIME.Положительные значения указывают абсолютное время.Обязательно используйте абсолютное время в формате UTC, поскольку система использует внутреннее время в формате UTC.Отрицательные значения указывают относительное время.

Таким образом, неправильное вычисленное время, похоже, дает значение, которое уже есть в прошлом.Это единственное разумное объяснение, почему SetWaitableTimer(m_hTimer, &liDueTime, 0, NULL, NULL, 0) возвращается немедленно.(OP проверил наличие ошибок и не распознал их.)


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

 uiMiliSeconds * -10000LL

, учитывая, что, например, VC использует 32 битадля int (даже для x64).Таким образом, из-за продвижения типа любое значение UINT32 расширилось бы до соответствующего long long без опасности переполнения.


RbMm пожаловался, что тип UINT32 для MiliSeconds может быть нетакой удачный выбор, поскольку он охватывает только поддиапазон времени, который может быть установлен.В случае OP этого поддиапазона может быть достаточно.В противном случае можно указать тип UINT64.

...