다음을 통해 공유


Threadpool Timer Functions

Download source code from MSDN Code Gallery.

The threadpool provides timer functionality with the SetThreadpoolTimer and CreateThreadpoolTimer functions; the ability to call your functions at specified times.  In this blog post I will show how to use the wrapper windowsthreadpool::Timer class provided to obtain timer functionality by using these APIs.  

Example code:

void CALLBACK Hello(PVOID state)

{

       UNREFERENCED_PARAMETER(state);

 

       cout << "Hello World - this should occur roughly every second." << endl;

}

 

int _cdecl _tmain()

{

       using namespace windowsthreadpool;

 

Timer<THREADPOOLCALLBACK> *t = new Timer<THREADPOOLCALLBACK>(1000, 1000, Hello); 

 

Sleep(10000);

 

delete t;

}

The function “Hello” will be called a 1000 milliseconds from now and from then on will repeatedly be called every 1000 milliseconds until the timer is destroyed by calling “delete t”. Since there is a Sleep(10000) before the delete call, the function “Hello” will be called ten times.

 

The timer class has four constructors which let you create timer objects and when the object goes out of scope the timer is destroyed or you can explicitly call DestroyTimer method to cancel the timer. The timer class accepts THREADPOOLCALLBACK as the template type; this type is defined as a function pointer accepting a PVOID as a parameter and returning nothing.  The timer class needs this definition because it needs to know the function signature of the callback method (Hello).

// Typedef for a threadpool function accepting one parameter and

// returning void

//

typedef void (CALLBACK* THREADPOOLCALLBACK)(PVOID);

 

The timer constructor accepts the starting time in milliseconds for the timer to expire, a period in milliseconds if the timer is expected to fire periodically, a function pointer to the callback and a pointer to the parameter if required. If the period is zero, the timer is a one-time timer and will fire only once.

To close the timer you can explicitly call DestroyTimer() or if the timer goes out of scope, then its destructor will close the timer object. To close a timer in the threadpool, you have to call SetThreadpoolTimer with the pftDueTime parameter set to NULL and the msPeriod and msWindowLength parameters set to 0. The timer object is then closed with CloseThreadpoolTimer.

void DestroyTimer()

{

       delete tcb;

       tcb = NULL;

}

 

// Destructor: cancels the timer when the object is destructed.

~Timer()

{

       if (tcb)

              DestroyTimer();

}

The timer class uses the TimerCallback class internally to create timer objects in the default process-wide threadpool, set the appropriate time and period and destroy the timer object when it goes out of scope.

 

template <class Function>

class TimerCallBack

{

private:

       const Function m_Func;

       PVOID state;

       PTP_TIMER timer;

 

       static void CALLBACK callback (PTP_CALLBACK_INSTANCE Instance, PVOID Param, PTP_TIMER timer)

       {

              TimerCallBack<Function> *pc = reinterpret_cast<TimerCallBack<Function>*>(Param);

              pc->m_Func(pc->state); // Call the user provided function

              return;

       }

 

public:

       TimerCallBack(const Function Func, PVOID st, PTP_CALLBACK_ENVIRON pEnv, size_t mSeconds, size_t Period) : m_Func(Func), state(st)

       {                   

              timer = CreateThreadpoolTimer(callback, this, pEnv);

              if (!timer)

              {

                     throw "Error: Could not create threadpool timer.";

              }

                    

              // A negative due time indicates a time relative to now.

              //

              FILETIME dueTime;

              *reinterpret_cast<PLONGLONG>(&dueTime) = -static_cast<LONGLONG>(MILLI_SECOND_TO_NANO100(mSeconds));

                                        

              SetThreadpoolTimer(timer, &dueTime, Period, ACCEPTABLELAG);

       }

 

       // Destructor - cancels the timer when the object is destructed.

       ~TimerCallBack()

       {

              // Prevent callbacks after timer is closed

              SetThreadpoolTimer(timer, NULL, 0, 0);

              CloseThreadpoolTimer(timer);

       }

 

Notice the ACCEPTABLELAG parameter to the SetThreadpoolTimer call. The last parameter is the amount of time that you can wait for this timer to fire once it expires. ACCEPTABLELAG is defined in timer.h.

 

Example: If you have two timers set within a process, one fires after 100milliseconds and another fires after 110milliseconds. Windows will now have to set off the first timer and 10mSecs later set off the next timer. However, if the first timer had set the last parameter to 20milliseconds which means it’s acceptable for the timer to fire as late as 120milliseconds from now, than Windows would only need to do work once. At time 110milliseconds from now, Windows will fire both the timers and both callbacks will run. This is generally known as timer coalescing and is useful for Windows to prevent unnecessary timers from firing and decreasing battery life. More information about timer coalescing available here.

Next up, how to call functions when events are fired…