c++ - Winapi 定时器回调线程,永不返回

标签 c++ c winapi

我必须调试一些不是我的代码。 这段代码使用 winapi Timer 接口(interface)实现了一个定时器 API。 我不太习惯这个 Winapi 功能,所以我需要你的帮助:)

据我了解,这段代码是这样完成的:

=> 初始化()

timerQueue = CreateTimerQueue();

=> 创建定时器()

CreateTimerQueueTimer(timerHandle, timerQueue, timerCallback, ..., WT_EXECUTEDEFAULT);

=> timerCallback()

 DeleteTimerQueueTimer(timerQueue , timerHandle,  NULL));
 calback() //Launch user-defined callback

=> CleanUp()//最后调用

DeleteTimerQueueEx(timerQueue , INVALID_HANDLE_VALUE);

当我们测试时,用户定义的回调会在所需时间后成功执行。但是在那之后 timerCallback 线程保持挂起状态并且永远不会返回,从而阻止所有进程返回。使用 VS 调试器,我可以在线程上看到那些线程(名为 TppWorkerThread@4)...

也许我们错过了一些让回调正确返回的东西,或者我们创建了某种死锁......但是我无法弄清楚......

如果我忘记了一些相关信息,请告诉我。

感谢您的帮助。

编辑:
更多信息:
- 阻塞线程在进程结束时处于此状态:
* 类别:工作线程
* 名称:_TppWorkerThread@4
* 位置:_ZwWaitForWorkViaWorkerFactory@8
* 优先级:正常

编辑2: 有了更多时间来处理这种奇怪的行为,我现在能够在独立代码中重现它。

#include <windows.h>
#include <stdio.h>

HANDLE gDoneEvent;
HANDLE hTimer[5];
HANDLE hTimerQueue = NULL;
HANDLE g_threadHandle;

void PeriodicCallback(void)
{
  printf("Periodic routine called.\n");
}

void SingleCallback(void)
{
  printf("Single routine called.\n");
  if (!DeleteTimerQueueTimer(hTimerQueue, hTimer[2], NULL))
    printf("DeleteTimerQueueTimer() fail. Return value is %d.\n", GetLastError());
}

void CALLBACK CommonCallback(PVOID lpParam, BOOLEAN TimerOrWaitFired)
{
  printf("Common routine called. Parameter is %d.\n", *(int *)lpParam);
  ((void (*)(void))lpParam)();

}

void MainTest(void)
{
  // Use an event object to track the TimerRoutine execution
  gDoneEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  if (NULL == gDoneEvent)
  {
    printf("CreateEvent failed (%d)\n", GetLastError());
    return -1;
  }

  if(0 == SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL))
  {
    printf("SetThreadPriority failed (%d)\n", GetLastError());
    return -2;
  }

  // Create the timer queue.
  hTimerQueue = CreateTimerQueue();
  if (NULL == hTimerQueue)
  {
    printf("CreateTimerQueue failed (%d)\n", GetLastError());
    return -3;
  }

/*
  if (!CreateTimerQueueTimer( &hTimer[2], hTimerQueue, 
    (WAITORTIMERCALLBACK)CommonCallback, &SingleCallback, 1000, 0, WT_EXECUTEDEFAULT))
  {
    printf("CreateTimerQueueTimer failed (%d)\n", GetLastError());
    return -4;
  }
*/

  if (!CreateTimerQueueTimer( &hTimer[4], hTimerQueue, 
    (WAITORTIMERCALLBACK)CommonCallback, &PeriodicCallback, 10, 500, WT_EXECUTEDEFAULT))
  {
    printf("CreateTimerQueueTimer failed (%d)\n", GetLastError());
    return -5;
  }

  // TODO: Do other useful work here 

  printf("Call timer routine in 10 seconds...\n");

  Sleep(4000);

  CloseHandle(gDoneEvent);

  if (!DeleteTimerQueueTimer(hTimerQueue, hTimer[4],  INVALID_HANDLE_VALUE))
    printf("DeleteTimerQueueTimer failed (%d)\n", GetLastError());
  // Delete all timers in the timer queue.
  if (!DeleteTimerQueueEx(hTimerQueue, INVALID_HANDLE_VALUE))
    printf("DeleteTimerQueue failed (%d)\n", GetLastError());

  Sleep(1000);

  ExitThread(0);
}

int main(int argc, char **argv[])
{
  if(g_threadHandle == CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)MainTest, NULL, 0, NULL))
    printf("Creation fail");

  ExitThread(0);
}

我正在 VisualStudio 2010 Professional 上编译这段代码。

似乎在调用 DeleteTimerQueueTimer() 之后,一些线程在线程池中保持挂起状态,从而阻止我的进程关闭。我还是想不通...

最佳答案

当您使用 INVALID_HANDLE_VALUE 作为第二个参数调用 DeleteTimerQueueEx 时,它将阻塞,直到所有正在运行的回调都完成。错误可能在您的一个永远不会返回的回调函数中。

您正在使用 NULL 作为第三个参数调用 DeleteTimerQueueTimer(timerQueue , timerHandle, NULL);,如果您删除计时器时回调正在运行,则不会等待回调完成。我建议使用 DeleteTimerQueueTimer(timerQueue , timerHandle, INVALID_HANDLE_VALUE) 它将阻塞直到回调完成(如果回调正在运行)。在不使用 DeleteTimerQueueTimer 的阻塞版本的情况下调用 cleanUp() 可能是一个错误,因为您可能会在执行回调的同时进行清理。

也可能是在回调中调用 DeleteTimerQueueExDeleteTimerQueueTimer 的问题,这是被禁止的。中断 DeleteTimerQueueEx 的执行并查看您所在的线程,如果它是一个 TppWorkerThread,那么您已经发现了您的错误。

编辑:

在您的评论中,您说您确实从回调中调用了 DeleteTimerQueueTimer 但没有使用 INVALID_HANDLE_VALUE,请再次阅读 http://msdn.microsoft.com/en-us/library/windows/desktop/ms682569%28v=vs.85%29.aspx 中的文档这似乎是合法的,但我清楚地记得我们做出设计决策是为了避免这种情况,很抱歉这么含糊,我希望有人能对此给出权威的建议。

我们向非计时器线程的队列发送一个事件/消息,然后删除计时器,您甚至可以为此设置一个专用线程,但这可能有点矫枉过正。在一天结束时,您需要确保在进行清理之前删除计时器,因此您必须在删除时阻塞或让其他线程在发出事件信号时执行此操作。

关于c++ - Winapi 定时器回调线程,永不返回,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24281395/

相关文章:

c++ - 用 fwrite 删除一些字符

c++ - Visual Studio 2013中的POSIX header (来自MinGW项目)

c - snprintf() 总是空终止吗?

c++ - 函数中的堆栈指针会是形参吗?

从 .lib 切换到 .so 后,Android 应用程序崩溃

c++ - 覆盖现有类的运算符

在不注册 WNDCLASS 的情况下创建窗口?

c++ - 在第三方窗口上显示/覆盖 QT 小部件(在 Windows 中)

c - sendto 中的参数无效

c++ - 错误 : LOWORD undeclared