Friday, November 16, 2007

WindowCE多线程编程

WindowCE多线程编程包括线程的启动、线程的运行状态控制、线程同步及数据通信和线程的正常/非正常退出。本项目的软件及架构在多线程设计上,要求通过多线程实现异步的数据采集及绘制,以提高系统运行效率。

Win32API支持多线程的启动,其中以前版本的程序直接调用API函数CreateThread()分配资源启动线程,并返回线程句柄(Handle),以控制线程状态。客观上,这种方法在Win32平台上是通用的。然而,通用不一定最好。

MFC是在EVC下提供的类库,基本上以MFC程序框架下封装了大多API系统调用,优点不仅仅是使用方便,更重要的是对系统的运行稳定性、正确性都提供的更高的保障。通过查阅得知,通过MFC提供的封装API函数AfxCreateThread()创建新线程,返回MFC的CWinThread类对象实例,这样通过对象实例可以更方便安全的进行线程的行为控制。

线程初始化时可以传递参数,在Window CE下线程函数标准声明为UINT FunctionName(PVOID pArg),在CreateThread时将pArg对应实参指定。但传参注意MFC不是线程安全类的指针。本系统传递CDialog指针,因此通过类型转换,即在CDialog子类方法中调用AfxBeginThread(QuerySampleThread,(LPVOID)this,0,0,0,0)。

线程的状态,一般有就绪态、运行台、阻塞态、终止态等。为了实现各线程的功能同步,有必要合理的是各个线程进行状态的切换。在MFC的线程框架下,通过::AfxCreateThread()启动线程、自身CWinTherad::Suspend()阻塞线程、其他线程调用CWinThread::Resume()重新就绪线程、线程return正常退出。考虑到本嵌入式系统对稳定性、效率的高要求,去掉不安全的调用,如CWinThread::Terminate()外部调用终止线程等方法。

WindowsCE提供若干线程通信机制,本系统选用Event(事件)进行线程间的同步。通过CreateEvent()申请Event资源,返回HANFLE实例,以进行行为控制。在申请时即可指定生成时Event状态以及复位机制。本系统中信号生成时为复位状态,有且仅有手动调用ResetEvent()才能复位——即手动方式,则在生成时参数为:CreateEvent(NULL,TRUE,FALSE,NULL)。

对线程控制通过判断各Event资源信号的置位/复位实现。一个特点就是Event资源的静态优先级:多Event的判断通过WaitForMultipleObjects(3,signals,FALSE,-1)实现,对于Event数组signals装入3个Event并按优先级排列HANDLE signals[3] = { hCloseEvent, hPauseSampleEvent, hBeginSampleEvent},如果三个Event同时置位,则首先响应hCloseEvent,实现了线程关闭的可靠性。

线程在某种条件下要进行状态切换,或者是自身行为或者是外界行为。首先线程外部创建线程,并指定线程初始状态。线程内部由于某种条件不满足,需要等待其他线程而挂起时自身调用CWindThread::Suspend()。然后需通过线程外界调用CWinThread::Resume(),使被唤醒线程进入就绪态,准备从CWinThread::Suspend()处继续执行。API的Sleep()可以是当前线程阻塞一定时间,在某些环境下(如与驱动交互时有意延时,以便驱动有时间操作硬件)可以用到。

对于线程的结束,MFC建议线程自身通过return正常结束,而TerminateThread()则简单的收回线程控制块资源,极可能造成县城内部资源不能释放,是系统资源泄漏、变得不稳定。

另外本系统为及时响应驱动的数据传递信号,将与之相关的线程QuerySampleThread提高优先级,通过CWindThread:: SetThreadPriority(THREAD_PRIORITY_ABOVE_NORMAL)实现。但Windows CE严格按优先级进行调度,因此QuerySampleThread一般在WaitForMultipleObjects()阻塞状态,才能使其他线程得以运行。

主要操作的代码为:

Ø 创建线程并设置线程优先级:
CWinThread *pQuerySampleThread = AfxBeginThread(QuerySampleThread,(LPVOID)this,0,0,0,0);
pQuerySampleThread->SetThreadPriority(THREAD_PRIORITY_ABOVE_NORMAL);

Ø 创建Event资源:
HANDLE hCloseEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
if ( hCloseEvent==INVALID_HANDLE_VALUE )
{ ……}

Ø Event的信号控制:
SetEvent(hCloseEvent);
… …
ResetEvent(hCloseEvent);

Ø 线程的Evnet判断及线程状态转换:
HANDLE signals[3] = { hCloseEvent, hPauseSampleEvent, hBeginSampleEvent};
DWORD dwEvent = WaitForMultipleObjects(3,signals,FALSE,-1);
if ( dwEvent ==0 ) { return; }
if ( dwEvent==1) { SuspendThread(pQuerySampleThread->m_hThread); }

Ø 线程的唤醒:
pResampleThread->ResumeThread();
以上提到WIndowCE下基本的多线程编程操作,可以满足小型项目的需要,但在具体开发中,要合理设计线程运行流程,防止并发运行中“与时间有关的错误”。