Windows CE下的多线程编程实验(转载自Stone& Ice)

来源:互联网 发布:linux 文件夹内容替换 编辑:程序博客网 时间:2024/06/05 20:51
Windows CE多线程编程包括线程的启动、线程的运行状态控制、线程同步及数据通信和线程的正常/非正常退出。本项目的软件及架构在多线程设计上,要求通过多线程实现异步的数据采集及绘制,以提高系统运行效率。
 
1.       线程的启动
Win32API提供支持多线程的启动,调用API函数CreateThread()分配资源启动线程,并返回线程句柄(Handle),以控制线程状态。客观上,这种方法在Win32平台上是通用的。然而,通用不一定最好。函数原型:<winbase.h>
HANDLE
WINAPI
CreateThread (
    LPSECURITY_ATTRIBUTES lpsa,    
// security属性,在WinCE下须为NULL
    DWORD   cbStack,                               // 堆栈大小,除非定义宏,否则被忽略
    LPTHREAD_START_ROUTINE lpStartAddr,    // 起始地址,即C的函数指针
    LPVOID lpvThreadParam,        // 自定义的传入线程参数
    DWORD fdwCreate,                  // 标识线程是否立即运行,默认是
LPDWORD lpIDThread                // 新线程ID
);

线程自身应用return结束,也可使用ExitThread()结束;主线程可以使用WaitForSingleObject(hThreadHandle, dwMilliseconds)等待线程结束,然后使用GetExitCode()获得返回码,最后使用CloseHandle()释放核心对象

Window C Runtime Library提供了封装CreateThread的函数_beginthreadex(),使得CRT对线程可以进行登记。函数原型一般在<process.h>中,但Windows SDK4.2没有对此函数的定义。
MFC是在eVC下提供的类库,基本上以MFC程序框架下封装了大多API系统调用,优点不仅仅是使用方便,更重要的是对系统的运行稳定性、正确性都提供的更高的保障。通过查阅得知,通过MFC提供的封装API函数AfxCreateThread()创建新线程,返回MFC的CWinThread类对象实例,这样通过对象实例可以更方便安全的进行线程的行为控制。函数原型为:<afxwin.h>
CWinThread* AFXAPI AfxBeginThread(      // 创建工作者线程重载方法
     AFX_THREADPROC pfnThreadProc,    // 线程起始函数
     LPVOID pParam,                                       // 自定义参数
     int nPriority = THREAD_PRIORITY_NORMAL,    // 优先级
     UINT nStackSize = 0,                                               // 栈大小
     DWORD dwCreateFlags = 0,                                //标识线程是否立即运行,默认是
     LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL    // 安全属性
);

CWinThread
* AFXAPI AfxBeginThread(    // 创建UI线程
     CRuntimeClass
* pThreadClass,    //指定线程类,为CWinThread子类
     int nPriority = THREAD_PRIORITY_NORMAL,
     UINT nStackSize 
= 0,
     DWORD dwCreateFlags 
= 0,
     LPSECURITY_ATTRIBUTES lpSecurityAttrs 
= NULL
);
线程初始化时可以传递参数,在Window CE下线程函数标准声明为UINT FunctionName(PVOID pArg),在CreateThread时将pArg对应实参指定。但传参注意MFC不是线程安全类的指针。本系统传递CDialog指针,因此通过类型转换,即在CDialog子类方法中调用AfxBeginThread(SubThread,(LPVOID)this,0,0,0,0)。
 
2.       线程的状态
线程的状态,一般有就绪态、运行台、阻塞态、终止态等。为了实现各线程的功能同步,有必要合理的是各个线程进行状态的切换。在MFC的线程框架下,通过::AfxCreateThread()启动线程、自身CWinTherad::Suspend()阻塞线程、其他线程调用CWinThread::Resume()重新就绪线程、线程return正常退出。考虑到本嵌入式系统对稳定性、效率的高要求,去掉不安全的调用,如CWinThread::Terminate()外部调用终止线程等方法。
对于线程的结束,MFC建议线程自身通过return正常结束,而TerminateThread()则简单的收回线程控制块资源,极可能造成线程内部资源不能释放,是系统资源泄漏、变得不稳定。
 
3.       线程的同步
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()可以是当前线程阻塞一定时间,在某些环境下(如与驱动交互时有意延时,以便驱动有时间操作硬件)可以用到。
 
4.       线程的优先级
本系统为及时响应驱动的数据传递信号,将与之相关的线程SubThread提高优先级,通过CWindThread:: SetThreadPriority(THREAD_PRIORITY_ABOVE_NORMAL)实现。但Windows CE严格按优先级进行调度,因此SubThread一般在WaitForMultipleObjects()阻塞状态,才能使其他线程得以运行。
 
5.       主要操作的代码示例为:
创建工作者线程并设置线程优先级:
CWinThread *pSubQuerySampleThread = AfxBeginThread(SubThread,(LPVOID)this,0,0,0,0);
pSubThread
->SetThreadPriority(THREAD_PRIORITY_ABOVE_NORMAL); 
创建Event资源:
HANDLE hCloseEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
if (INVALID_HANDLE_VALUE == hCloseEvent) {... ...}
Event的信号控制:
SetEvent(hCloseEvent);
… …
ResetEvent(hCloseEvent);
线程的Event判断及线程状态转换:
HANDLE signals[3= { hCloseEvent, hPauseEvent, hBeginEvent}; 
DWORD dwEvent 
= WaitForMultipleObjects(3,signals,FALSE,-1);
if (WAIT_OBJECT_0 == dwEvent)   {
       return
}
if (WAIT_OBJECT_0+1 == dwEvent)   { 
       SuspendThread(pSubThread->m_hThread); 
}
线程的唤醒:
pSubThread->ResumeThread();
6.       使用MFC进行线程同步(更新于2008-04)
原工程进行改版,软件在原有基础上更合理进行封装,并使用了MFC包装的Event和WaitForMultipleObject,即CEvent和CMultiLock。
创建CEvent,构造函数:需包含<afxmt.h>
CEvent::CEvent(
     BOOL bInitiallyOwn 
= FALSE,    // 标识初始时是否有信号
     BOOL bManualReset = FALSE,    // 标识是否为手动置位
     LPCTSTR lpszNAme = NULL,    // 在进程间同步是可以指定名字
     LPSECURITY_ATTRIBUTES lpsaAttribute = NULL     // 安全标识
);
创建所有的CEvent实例后,可以初始化CSyncObject数组,以供CMultiLock使用。
创建CMultiLock实例,指明同步的信号,构造函数:<afxmt.h>
CMultiLock::CMultiLock(
     CSyncObject
* ppObjects[],         // 同步对象数组
     DWORD dwCount,                      // 同步对象个数
     BOOL bInitialLock = FALSE      // 标识初始时是否访问对象
);
CMultiLock可以应用于CEvent、CMutex、CCriticalSection。但对于CEvent并不存在对象锁定的概念,使用CMultiLock::Lock()方法可以等待CEvent实例激发,类似于WaitForMultipleObject()。注意微软已确认同步对象数组大小若大于8,则会出现内存泄漏(见参考文献)。
使用MFC同步对象代码示例:
// 自定义一个用于线程同步的类EventLock,并声明一些CEvent静态成员:
#define EVNT_CNT 3
class EventLock
{
public:
    DWORD WaitForEvents();
    
void SetEvent(int iNum);
    
void ResetEvent(int iNum);
    EventLock();
    
virtual ~EventLock();
    
static CEvent *pevnt[EVNT_CNT];    // EVNT_CNT标识事件对象数
private:
    
static CEvent evntClose, evntBegin, evntPause;
    CMultiLock 
*pLock;
}
;

// 创建CEvent实例:
CEvent EventLock::evntClose (FALSE,TRUE);
CEvent EventLock::evntBegin (FALSE,TRUE);
CEvent EventLock::evntPause (FALSE);
// 初始化同步对象数组:
CEvent * EventLock::pevnt[]={&evntClose,&evntBeginSample,
                             
&evntPauseSample, &evntPreSample}
;

// 在构造器中创建CMultiLock实例:
EventLock::EventLock()
{
    pLock 
= new CMultiLock((CSyncObject **)EventLock::pevnt,EVNT_CNT);
}

EventLock::
~EventLock()
{
    delete pLock;
}


// 与Event相同的MFC CEvent置位、复位操作:
void EventLock::ResetEvent( int iNum )
{
    ASSERT(
0<=iNum && iNum<EVNT_CNT);
    pevnt[iNum]
->ResetEvent();
}

void EventLock::SetEvent(int iNum)
{
    ASSERT(
0<=iNum && iNum<EVNT_CNT);
    pevnt[iNum]
->SetEvent();
}

// 使用CMultiLock::Lock()等待事件激发,此处为等待任意事件激发:
DWORD EventLock::WaitForEvents()
{
    DWORD dwEvent 
= 0;
    dwEvent 
= pLock->Lock(INFINITE, FALSE);    // 第二个参数为TRUE则等待全部事件
    dwEvent -= WAIT_OBJECT_0;
    
return dwEvent;
}

以上提到Windows CE下基本的多线程编程操作,可以满足小型项目的需要,但在具体开发中,要合理设计线程运行流程,防止并发运行中“与时间有关的错误”。
(文中源码均取自真实项目,在Mcrosoft embedded Visual C++ 4.0 SP4下编译通过,于ARM9+Windows CE4.2环境下运行正确稳定。)
                    Stone&Ice
                    From: http://blog.csdn.net/stoneandice
参考文献:
1.       《Multithreading Applications in Win32》Jim Beveridge & Robert Wiener 侯捷译 书中详细讲述在Win32环境下进行多线程编程的方法及设计原则
2.       http://support.microsoft.com/kb/151033/en-us/ Microsoft证实使用CMultiLock可能产生内存泄漏并提供权宜方法
3.      http://msdn2.microsoft.com/en-us/library/ MSDN提供权威的MFC多线程编程相关方法
原创粉丝点击