WinCE下多线程编程

 1、基础知识:

1)进程 (process):是一个具有一定功能的程序在一个数据集合的一次动态执行过程。进程由正文段,用户数据段以及系统数据段共同组成一个执行环境,与处理器、存储器和外设等资源的分配和回收相对应,进程是计算机系统资源的使用主体,也是操作系统分配资源的基本单位。

2)线程:在多个进程并发执行时,进程切换的开销比较大,影响了进程间通信的效率。因此提出了更小的能独立运行的基本单位——线程。线程时进程的一个实体,是cpu调度和分配的基本单位,除了一些在运行中必不可少的资源,线程不拥有系统资源,但是线程可以和同属于一个进程的其他线程共享进程的全部资源。

3)传统的操作系统中,进程是分配资源、独立调度和分配的基本单位,引入线程后,线程当作调度和分配的基本单位,进程仍然是拥有资源的基本单位。

4)关系:为了让进程完成一些工作,进程必须至少占有一个线程,所以线程是描述进程内的执行,正是线程负责执行包含在进程的地址空间的代码。

5)互锁函数: 运行在用户模式,它能保证当一个线程访问一个变量时,其他线程无法访问此变量,以确保变量值的唯一性。这种访问方式叫做原子访问。如InterlockedIncrement(LPLONG)等等。

6)事件对象:事件对象运行在内核模式,利用等待函数来等待所需的事件、信号,在等待的过程中,线程处于睡眠态,当接收到信号后,内核恢复线程的运行。等待函数如:WaitForSingleObject、WaitForMultipleObjects等四个。和事件有关的函数:CreateEvent、SetEvent  、PulseEvent  、ResetEvent 、OpenEvent等。

其中,CreateEvent创建一个事件对象,参数1必须为NULL,参数2指定是否手工重新设置事件对象的状态,如果为FALSE,则当等待函数接到信号并返回后此事件对象被自动置为无效,这时等待此事件对象的其他线程就不会被唤醒;如果为TRUE,则不会被置为无效,其他等待此事件对象的的线程也将被唤醒。

ResetEvent函数可以手工将事件对象置为无效。

SetEvent函数将事件对象置为有效。

OpenEvent打开已经创建的事件对象,一般用于不同进程内的线程同步。

2、线程的编程技术

1)编写线程函数,其必须有如小原型:

DWORD WINAPI MyThread(LPVOID lpvThreadParm);

注意只能有一个参数,这个函数不能由用户调用,由操作系统调用一个内部函数如StartOfThread。

2)创建线程:一个线程的主线程由操作系统自动生成,由主线程创建其他线程用CreateThread函数。

3)终止线程:某线程调用了ExitThread函数,终止自己;调用TerminateThread函数可以终止指定的线程。

4)其他函数:设定优先级SetThreadPriority、挂起线程SuspendThread、恢复线程ResumeThread。

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环境下运行正确稳定。)


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/xuechen_gemgirl/archive/2009/05/19/4201964.aspx

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值