我正在创建如下的手动等待计时器
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计时器会停止并重新激活计时器,因此它不应处于信号状态,而应仅在给定时间后才发出信号。我在这里想念什么?
答案 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
因此,解决方案是在乘法之前将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
。