最近在使用定时器的时候,被一个小细节坑了,偶尔导致 coredump,费了好大的力气才找到原因,现在整理一下备忘。

我采用了如下方式来生成了定时器:

if (!CreateTimerQueueTimer( &m_hTimer, m_hTimerQueue, 
    (WAITORTIMERCALLBACK)videoTimer_proc, this, interval, 0, 0))
{
    printf("CreateTimerQueueTimer failed (%d)", GetLastError());
    return false;
}

然后,我就不断地调用上面的代码来生成定时器,并干相应的的事情,比如说视频帧的刷新等操作。但是,我犯了一个错误,在我需要结束定时操作后,我采用了如下的方式来结束定时器:

if (m_hTimer)
{
    DeleteTimerQueueTimer(m_hTimerQueue, m_hTimer, NULL);
    m_hTimer = NULL;
}

就是因为最后一个参数的原因,偶尔会出现崩溃:即定时器并没有因为这一行代码而完全销毁,从而导致这个类在销毁后,定时器依然触发了回调信息。

要解决这个问题很简单,只需要把上述代码里的 NULL 参数修改为 INVALID_HANDLE_VALUE 即可:

if (m_hTimer)
{
    DeleteTimerQueueTimer(m_hTimerQueue, m_hTimer, INVALID_HANDLE_VALUE);
    m_hTimer = NULL;
}

看看微软官方文档对这两个参数区别的说明:

If this parameter is INVALID_HANDLE_VALUE, the function waits for any running timer callback functions to complete before returning.

If this parameter is NULL, the function marks the timer for deletion and returns immediately. If the timer has already expired, the timer callback function will run to completion. However, there is no notification sent when the timer callback function has completed. Most callers should not use this option, and should wait for running timer callback functions to complete so they can perform any needed cleanup.

即,如果使用 INVALID_HANDLE_VALUE 这个参数,则 DeleteTimerQueueTimer 会等待定时器销毁以及回调函数结束后才返回;如果使用 NULL 则把定时器立即标记为销毁,但是实际上的运行并不受控制,如果有定时器正处在回调函数里并正在执行,有可能导致异常情况。

我的崩溃就是由于这个参数引起的,不容易出现,很不好找原因。