Winapi定时器回调线程,永远不会返回
Winapi Timer callback thread, never returns
我必须调试一些不是我的代码。这段代码使用winapi定时器接口实现了一个定时器API。我不太习惯这个Winapi功能,所以我可以使用您的帮助:)
根据我的理解,这段代码是这样完成的:
Init ()=>
timerQueue = CreateTimerQueue();
=> CreateTimer ()
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
*优先级:Normal
EDIT2:有了更多的时间来处理这个奇怪的行为,我现在可以在一个独立的代码中重现它了。
#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
时,它将阻塞,直到所有正在运行的回调都完成。错误可能在一个永远不会返回的回调函数中。
您正在调用DeleteTimerQueueTimer(timerQueue , timerHandle, NULL);
与NULL作为第三个参数,这将不会等待回调完成,如果一个正在运行时,你删除计时器。我建议使用DeleteTimerQueueTimer(timerQueue , timerHandle, INVALID_HANDLE_VALUE
),它将阻塞,直到回调完成(如果一个正在运行)。调用cleanUp()而不使用DeleteTimerQueueTimer
的阻塞版本很可能是一个bug,因为你可能在回调执行的同时进行清理。
这也可能是在回调中调用DeleteTimerQueueEx
或DeleteTimerQueueTimer
的问题,这是禁止的。中断DeleteTimerQueueEx的执行,看看你在哪个线程,如果它是TppWorkerThread,那么你已经找到了你的错误。
在您的评论中,您说您确实从回调中调用DeleteTimerQueueTimer
,但不使用INVALID_HANDLE_VALUE
,再次从http://msdn.microsoft.com/en-us/library/windows/desktop/ms682569%28v=vs.85%29.aspx阅读文档,这似乎是合法的,但我清楚地记得我们做出设计决策以避免这种情况,我很抱歉这是如此模糊,我希望有人能对此给出权威的建议。
我们发送一个事件/消息到一个非计时器线程的队列,然后删除计时器,你甚至可以有一个专门的线程,但这可能是多余的。在一天结束时,您需要确保在进行清理之前删除计时器,因此您必须在删除时阻塞,或者在事件发出信号时让其他线程执行此操作。
在对那个问题进行了一些研究之后,我想我得到了答案。看来这个timerQueue API是在threadPool winAPI之上编码的,当我们要求创建一个timerQueue窗口时,创建一个线程池,所有回调都将从那里启动。直到这里没有问题,但是,当我们要求删除timerQueue时,似乎这个线程池没有被删除…这将导致某些线程保持挂起等待使用,并阻止进程返回。
一段时间后(超时??),这些线程返回,进程退出。
我真的不明白为什么这个游泳池没有关闭…但是,现在,我使用了一个变通方法:
exit(0);
在我的程序结束时,这有点残酷,但它完成了工作(即:杀死我的进程,无论线程是否仍然挂起)
- 为什么是谷神星协方差.计算()似乎永远运行而不返回?
- cudaMemcpyToSymbol只是挂起,永远不会返回.GPU 处理速度为 100%.代码在 K40 上工作正常,但
- 无法从串行端口读取.WaitCommEvent() 永远不会返回
- 程序返回-1073741571而不是永远进行的值
- OpenRAVE ControllerBase 在 IsDone() 方法处阻塞并且永远不会返回
- LoadLibrary 永远不会在 Visual Studio 2013 c++ 项目中返回
- 如果发送进程停止,boost::interprocess::timed_received()永远不会返回
- CoCreateInstance() 永远不会返回
- 为什么这个对isspace()的调用永远不会返回true
- 对 Boost JSON 解析器的调用永远不会返回
- 为什么 ReadFile() 不返回 0 ?程序尝试永远从管道读取数据
- waveOutWrite 缓冲区永远不会返回到应用程序
- Poco C++ 1.4.3p1 的 DatagramSocket ReceiveBytes() 永远不会返回。我是否滥用了该功能?
- 为什么从 write() 调用 __kernel_vsyscall() 永远不会返回
- ICLRuntimeHost Start方法永远不会返回
- 为什么eof()永远不会返回true
- io_service::run() 永远不会返回
- 如何永远从"ACE_Reactor::instance()->run_reactor_event_loop();"街区返回?
- Winapi定时器回调线程,永远不会返回
- Boost read()永远不会返回,即使在服务器上执行写(ssl) Boost