最近研读网狐的代码,看到了CTimerEngine定时器引擎这块,做个笔记,欢迎交流!
定时器引擎(CTimerEngine)的内部结构
内部包含两个变量:定时器线程(CTimerThread)和通知组件(CQueueServiceEvent)。
调用流程
- 首先调用【CTimerEngine::BeginService】;
- 调用InitThread设置每次的时间间隔;
- 调用定时器线程的StartThead函数,其实是调用了基类线程的CServiceThread::StartThead函数;
- 结束。
CServiceThread::StartThead剖析
1.通过CreateEvent创建一个无信号的事件;
2. _beginthreadex启动一个线程,线程函数为ThreadFunction;
3. WaitForSingleObject这个等待事件函数不会挂起,因为线程函数ThreadFunction里面,设置了有信号状态。
4. 关闭事件CloseHandle。当前线程还在无限循环处理中。
线程函数【CServiceThread::ThreadFunction】剖析
1.通过传入的void*参数,强制类型转为对应的对象指针,从而得到事件的HANDLE句柄;
2.通过SetEvent设置事件有信号了,那么刚才提到的WaitForSingleObject就不会阻塞等待了,WaitForSingleObject下面的主逻辑该干啥就去干啥了。
3.启动【while (pThread->m_bRun)】循环;
4.调用RepetitionRun这个运行函数,此处是关键所在。因为RepetitionRun函数在CServiceThread里面是纯虚函数:
virtual bool RepetitionRun()=NULL;
那么此处实际调用的是子类的运行函数(即);
bool CTimerThread::RepetitionRun()
{
ASSERT(m_pTimerEngine!=NULL);
Sleep(m_dwTimerSpace);
m_pTimerEngine->OnTimerThreadSink();
return true;
}
注意:里面有个Sleep休眠函数,以此来实现心跳间隔;
5.在子类的运行函数里面,通过定时器引擎的指针,来调用OnTimerThreadSink这个定时器通知函数:
void CTimerEngine::OnTimerThreadSink()
在这个函数时候,才是真正要做的事情,分步流程:
5.1 通过下述函数来发送通知(这是个定时器事件),
bool CQueueServiceEvent::PostTimerEvent(WORD wTimerID, WPARAM wBindParam)
然后调用队列服务,把这个定时器事件添加到队列;
备注:本文一开始就提到了,定时器引擎内部有一个通知组件(CQueueServiceEvent)的变量。
5.2 通过CQueueService::AddToQueue来投递信息,当然CQueueServiceEvent里面有个IQueueService的指针,这个指针里面有个接口AddToQueue,这个接口可以向队列加入数据,实现代码如下:
bool __cdecl CQueueService::AddToQueue(WORD wIdentifier, void * const pBuffer, WORD wDataSize)
{
CThreadLockHandle LockHandle(&m_ThreadLock);
m_DataStorage.AddData(wIdentifier,pBuffer,wDataSize);
PostQueuedCompletionStatus(m_hCompletionPort,wDataSize,(ULONG_PTR)this,NULL);
return true;
}
5.3 系统函数PostQueuedCompletionStatus设置完成端口;
5.4 在队列服务线程里面也有个实时的运行函数在监听,监听是通过GetQueuedCompletionStatus来等待完成端口。获得到数据就该调度具体处理了。
6 其实,通过PostQueuedCompletionStatus–>>PostQueuedCompletionStatus就实现了跨线程的跳转了。这也是线程通讯的一个主要方式。
关于5.2里面提到的IQueueService指针的由来进行补充说明:
在启动定时器引擎服务前,先要把定时器引擎与调度引擎进行绑定,即调用下述函数:
bool __cdecl CTimerEngine::SetTimerEngineSink(IUnknownEx * pIUnknownEx)
{
//效验参数
ASSERT(pIUnknownEx!=NULL);
ASSERT(m_bService==false);
if (m_bService==true) return false;
if (pIUnknownEx==NULL) return false;
//设置接口
if (m_AttemperEvent.SetQueueService(pIUnknownEx)==false)
{
CEventTrace::ShowEventNotify(TEXT("定时器引擎与触发服务绑定失败"),Level_Exception);
return false;
}
return true;
}
关键技术总结
线程通信、事件句柄、调度队列、完成端口
待续...