none
如何在非Windows线程构建定时器回掉? RRS feed

  • 问题

  • 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;
    }

    2016年12月7日 7:23

全部回复

  • 其实在工作线程中您可以设置消息循环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.

    2016年12月8日 6:20
    版主
  • 如果不想在工作线程中设置消息循环也可以,用内核对象来做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.

    2016年12月8日 6:23
    版主
  • 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.

    2016年12月8日 6:32
    版主