等待计时器不等待指定的时间段

时间:2019-02-21 05:50:33

标签: c++ windows winapi msdn

我正在创建如下的手动等待计时器

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

我观察到在延迟10秒钟(或5秒钟)调用InduceSleep()时,waitforsingleobject正在立即返回WAIT_OBJECT_0而没有任何延迟,因此会立即向计时器发出信号。在文档中提到,setwaitable计时器会停止并重新激活计时器,因此它不应处于信号状态,而应仅在给定时间后才发出信号。我在这里想念什么?

1 个答案:

答案 0 :(得分:2)

这是一个简单的错误:

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

Live Demo on coliru

因此,解决方案是在乘法之前将uiMiliseconds转换为有符号整数(就像我在第二行输出中所做的那样)。

故事的其余部分可能对OP很明显。来自MSDN的有关SetWaitableTimer function的信息:

  

lpDueTime

     

以100纳秒为间隔将计时器状态设置为发出信号的时间。使用FILETIME结构描述的格式。正值表示绝对时间。请确保使用基于UTC的绝对时间,因为系统在内部使用基于UTC的时间。负值表示相对时间。

因此,错误的计算时间似乎生成的值已经过去。这是SetWaitableTimer(m_hTimer, &liDueTime, 0, NULL, NULL, 0)立即返回的唯一合理解释。 (OP检查了错误,但没有发现错误。)


为使其“防弹”,我建议

 uiMiliSeconds * -10000LL

考虑到这一点VC为int使用32位(甚至对于x64)。因此,由于类型提升,任何UINT32值都将扩展为相应的long long而没有溢出的危险。


RbMm抱怨说UINT32的类型MiliSeconds并不是很幸运的选择,因为它只覆盖可以设置的子时间范围。对于OP,此子范围可能就足够了。否则,可能会考虑键入UINT64