询问者
如何在非Windows线程构建定时器回掉?

问题
-
MFC程序,自己创建了一个线程A,非Windows线程。
现在需要实现一个定时器,由线程A启动,回调函数被调用时做一些处理。
现在用SetTimer/KillTimer组合,发现回掉函数没有执行,后来查询一下,似乎SetTimer/KillTimer回向线程发送一个WM_TIMER消息,因此必须是Window线程,必须有消息队列。但是线程A是普通线程,没有线程队列,收不到WM_TIMER消息。
后来想用等待时钟,WaitTimer。用CreateWaitableTimer/SetWaitableTimer,设置回掉函数。发现还是没法触发回掉函数。
自己写了一个列子,在main主函数中调用SleepEx(INFINITE, TRUE); 的话,回掉函数可以调用。
如果把SleepEx替换成Sleep的话,回调函数就没触发。搞不懂什么原因,似乎SleepEx把线程状态置为阻塞?
我的线程A是一个主工作线程,不能阻塞或者SleepEx睡眠等待。这种情况下,如何利用异步回调函数?
#define UNICODE 1 #define _UNICODE 1 #pragma comment(lib, "user32.lib") #include <windows.h> #include <stdio.h> #include <tchar.h> #define _SECOND 10000000 typedef struct _MYDATA { TCHAR *szText; DWORD dwValue; } MYDATA; VOID CALLBACK TimerAPCProc( LPVOID lpArg, // Data value DWORD dwTimerLowValue, // Timer low value DWORD dwTimerHighValue ) // Timer high value { // Formal parameters not used in this example. UNREFERENCED_PARAMETER(dwTimerLowValue); UNREFERENCED_PARAMETER(dwTimerHighValue); MYDATA *pMyData = (MYDATA *)lpArg; _tprintf( TEXT("Message: %s\nValue: %d\n\n"), pMyData->szText, pMyData->dwValue ); MessageBeep(0); } int main( void ) { HANDLE hTimer; BOOL bSuccess; __int64 qwDueTime; LARGE_INTEGER liDueTime; MYDATA MyData; MyData.szText = TEXT("This is my data"); MyData.dwValue = 100; hTimer = CreateWaitableTimer( NULL, // Default security attributes FALSE, // Create auto-reset timer TEXT("MyTimer")); // Name of waitable timer if (hTimer != NULL) { __try { // Create an integer that will be used to signal the timer // 5 seconds from now. qwDueTime = -5 * _SECOND; // Copy the relative time into a LARGE_INTEGER. liDueTime.LowPart = (DWORD) ( qwDueTime & 0xFFFFFFFF ); liDueTime.HighPart = (LONG) ( qwDueTime >> 32 ); bSuccess = SetWaitableTimer( hTimer, // Handle to the timer object &liDueTime, // When timer will become signaled 2000, // Periodic timer interval of 2 seconds TimerAPCProc, // Completion routine &MyData, // Argument to the completion routine FALSE ); // Do not restore a suspended system if ( bSuccess ) { for ( ; MyData.dwValue < 1000; MyData.dwValue += 100 ) { SleepEx( INFINITE, // Wait forever TRUE ); // Put thread in an alertable state } } else { printf("SetWaitableTimer failed with error %d\n", GetLastError()); } } __finally { CloseHandle( hTimer ); } } else { printf("CreateWaitableTimer failed with error %d\n", GetLastError()); } return 0; }
全部回复
-
其实在工作线程中您可以设置消息循环SetTimer/KillTimer就可以正常工作了。
#include <Windows.h> #include <iostream> #include <process.h> using namespace std; VOID CALLBACK TimerProc( _In_ HWND hwnd, _In_ UINT uMsg, _In_ UINT_PTR idEvent, _In_ DWORD dwTime ) { static size_t nCount = 0; cout << nCount++ << " : Timeout" << endl; } unsigned __stdcall ThreadProc(void* lParam) { SetTimer(NULL, 1, 1000, TimerProc); MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } KillTimer(NULL, 1); return 0; } int main() { HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, ThreadProc, NULL, 0, NULL); WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); return 0; }
Visual C++ enthusiast, like network programming and driver development. At present is being engaged in the WinCE/Windows Mobile platform embedded development.
-
如果不想在工作线程中设置消息循环也可以,用内核对象来做WaitForSingleObject的操作也可以达到SetTimer的效果。
// 线程函数中 unsigned __stdcall ThreadProc(void* lParam) { while (WAIT_TIMEOUT == WaitForSingleObject(hEvent, dwTimeout)) { // TODO: } return 0; }
Visual C++ enthusiast, like network programming and driver development. At present is being engaged in the WinCE/Windows Mobile platform embedded development.
-
DWORD WINAPI SleepEx( _In_ DWORD dwMilliseconds, _In_ BOOL bAlertable );
这个与SleepEx的第二个参数bAlertable有关,如果将其设置为FALSE,您看看回调函数还能正常响应吗?将第二个参数设置为FALSE,实际就与Sleep调用一样了。
详细说明请参考MSDN文档。Visual C++ enthusiast, like network programming and driver development. At present is being engaged in the WinCE/Windows Mobile platform embedded development.