电脑故障

位置:IT落伍者 >> 电脑故障 >> 浏览文章

Windows平台下的多线程编程


发布日期:2023/9/3
 

线程是进程的一条执行路径它包含独立的堆栈和CPU寄存器状态每个线程共享所有的进程资源包括打开的文件信号标识及动态分配的内存等一个进程内的所有线程使用同一个地址空间而这些线程的执行由系统调度程序控制调度程序决定哪个线程可执行以及什么时候执行线程线程有优先级别优先权较低的线程必须等到优先权较高的线程执行完后再执行在多处理器的机器上调度程序可将多个线程放到不同的处理器上去运行这样可使处理器任务平衡并提高系统的运行效率

Windows是一种多任务的操作系统在Windows的一个进程内包含一个或多个线程位Windows环境下的Win API提供了多线程应用程序开发所需要的接口函数而利用VC中提供的标准C库也可以开发多线程应用程序相应的MFC类库封装了多线程编程的类用户在开发时可根据应用程序的需要和特点选择相应的工具为了使大家能全面地了解Windows多线程编程技术本文将重点介绍Win API和MFC两种方式下如何编制多线程程序

多线程编程在Win方式下和MFC类库支持下的原理是一致的进程的主线程在任何需要的时候都可以创建新的线程当线程执行完后自动终止线程; 当进程结束后所有的线程都终止所有活动的线程共享进程的资源因此在编程时需要考虑在多个线程访问同一资源时产生沖突的问题当一个线程正在访问某进程对象而另一个线程要改变该对象就可能会产生错误的结果编程时要解决这个沖突

Win API下的多线程编程

Win API是Windows操作系统内核与应用程序之间的界面它将内核提供的功能进行函数包装应用程序通过调用相关函数而获得相应的系统功能为了向应用程序提供多线程功能Win API函数集中提供了一些处理多线程程序的函数集直接用Win API进行程序设计具有很多优点: 基于Win的应用程序执行代码小运行效率高但是它要求程序员编写的代码较多且需要管理所有系统提供给程序的资源用Win API直接编写程序要求程序员对Windows系统内核有一定的了解会占用程序员很多时间对系统资源进行管理因而程序员的工作效率降低

用Win函数创建和终止线程

Win函数库中提供了操作多线程的函数包括创建线程终止线程建立互斥区等在应用程序的主线程或者其他活动线程中创建新的线程的函数如下

HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributesDWORD dwStackSizeLPTHREAD_START_ROUTINE lpStartAddressLPVOID lpParameterDWORD dwCreationFlagsLPDWORD lpThreadId);

如果创建成功则返回线程的句柄否则返回NULL创建了新的线程后该线程就开始启动执行了但如果在dwCreationFlags中使用了CREATE_SUSPENDED特性那么线程并不马上执行而是先挂起等到调用ResumeThread后才开始启动线程在这个过程中可以调用下面这个函数来设置线程的优先权

BOOL SetThreadPriority(HANDLE hThreadint nPriority);

当调用线程的函数返回后线程自动终止如果需要在线程的执行过程中终止则可调用函数

VOID ExitThread(DWORD dwExitCode);

如果在线程的外面终止线程则可调用下面的函数

BOOL TerminateThread(HANDLE hThreadDWORD dwExitCode);

但应注意: 该函数可能会引起系统不稳定而且线程所占用的资源也不释放因此一般情况下建议不要使用该函数

如果要终止的线程是进程内的最后一个线程则线程被终止后相应的进程也应终止

线程的同步

在线程体内如果该线程完全独立与其他线程没有数据存取等资源操作上的沖突则可按照通常单线程的方法进行编程但是在多线程处理时情况常常不是这样线程之间经常要同时访问一些资源由于对共享资源进行访问引起沖突是不可避免的为了解决这种线程同步问题Win API提供了多种同步控制对象来帮助程序员解决共享资源访问沖突在介绍这些同步对象之前先介绍一下等待函数因为所有控制对象的访问控制都要用到这个函数

Win API提供了一组能使线程阻塞其自身执行的等待函数这些函数在其参数中的一个或多个同步对象产生了信号或者超过规定的等待时间才会返回在等待函数未返回时线程处于等待状态此时线程只消耗很少的CPU时间使用等待函数既可以保证线程的同步又可以提高程序的运行效率最常用的等待函数是

DWORD WaitForSingleObject(HANDLE hHandleDWORD dwMilliseconds);

而函数WaitForMultipleObject可以用来同时监测多个同步对象该函数的声明为

DWORD WaitForMultipleObject(DWORD nCountCONST HANDLE *lpHandlesBOOL bWaitAllDWORD dwMilliseconds);

)互斥体对象

Mutex对象的状态在它不被任何线程拥有时才有信号而当它被拥有时则无信号Mutex对象很适合用来协调多个线程对共享资源的互斥访问可按下列步骤使用该对象

首先建立互斥体对象得到句柄

HANDLE CreateMutex();

然后在线程可能产生沖突的区域前(即访问共享资源之前)调用WaitForSingleObject将句柄传给函数请求占用互斥对象

dwWaitResult = WaitForSingleObject(hMutexL);

共享资源访问结束释放对互斥体对象的占用

ReleaseMutex(hMutex);

互斥体对象在同一时刻只能被一个线程占用当互斥体对象被一个线程占用时若有另一线程想占用它则必须等到前一线程释放后才能成功

)信号对象

信号对象允许同时对多个线程共享资源进行访问在创建对象时指定最大可同时访问的线程数当一个线程申请访问成功后信号对象中的计数器减一调用ReleaseSemaphore函数后信号对象中的计数器加一其中计数器值大于或等于0但小于或等于创建时指定的最大值如果一个应用在创建一个信号对象时将其计数器的初始值设为0就阻塞了其他线程保护了资源等初始化完成后调用ReleaseSemaphore函数将其计数器增加至最大值则可进行正常的存取访问可按下列步骤使用该对象

首先创建信号对象

HANDLE CreateSemaphore();

或者打开一个信号对象

HANDLE OpenSemaphore();

然后在线程访问共享资源之前调用WaitForSingleObject

共享资源访问完成后应释放对信号对象的占用

ReleaseSemaphore();

)事件对象

事件对象(Event)是最简单的同步对象它包括有信号和无信号两种状态在线程访问某一资源之前需要等待某一事件的发生这时用事件对象最合适例如只有在通信端口缓沖区收到数据后监视线程才被激活

事件对象是用CreateEvent函数建立的该函数可以指定事件对象的类和事件的初始状态如果是手工重置事件那么它总是保持有信号状态直到用ResetEvent函数重置成无信号的事件如果是自动重置事件那么它的状态在单个等待线程释放后会自动变为无信号的用SetEvent可以把事件对象设置成有信号状态在建立事件时可以为对象命名这样其他进程中的线程可以用OpenEvent函数打开指定名字的事件对象句柄

)排斥区对象

在排斥区中异步执行时它只能在同一进程的线程之间共享资源处理虽然此时上面介绍的几种方法均可使用但是使用排斥区的方法则使同步管理的效率更高

使用时先定义一个CRITICAL_SECTION结构的排斥区对象在进程使用之前调用如下函数对对象进行初始化:

VOID InitializeCriticalSection(LPCRITICAL_SECTION);

当一个线程使用排斥区时调用函数EnterCriticalSection或者TryEnterCriticalSection;

当要求占用退出排斥区时调用函数LeaveCriticalSection释放对排斥区对象的占用供其他线程使用

基于MFC的多线程编程

MFC是微软的VC开发集成环境中提供给程序员的基础函数库它用类库的方式将Win API进行封装以类的方式提供给开发者由于其快速简捷功能强大等特点深受广大开发者喜爱因此建议使用MFC类库进行应用程序的开发

在VC++附带的MFC类库中提供了对多线程编程的支持基本原理与基于Win API的设计一致但由于MFC对同步对象做了封装因此实现起来更加方便避免了对象句柄管理上的烦琐工作

在MFC中线程分为两种工作线程和用户接口线程工作线程与前面所述的线程一致用户接口线程是一种能够接收用户的输入处理事件和消息的线程

工作线程

工作线程编程较为简单设计思路与前面所讲的基本一致: 一个基本函数代表了一个线程创建并启动线程后线程进入运行状态; 如果线程用到共享资源则需要进行资源同步处理这种方式创建线程并启动线程时可调用函数

CWinThread*AfxBeginThread( AFX_THREADPROC pfnThreadProc LPVOID pParamint nPriority= THREAD_PRIORITY_NORMALUINT nStackSize =DWORD dwCreateFlags= LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL);参数pfnThreadProc是线程执行体函数函数原形为: UINT ThreadFunction( LPVOID pParam)

参数pParam是传递给执行函数的参数 <

上一篇:Windows7内存占用大的原因

下一篇:流程图来了:如何将Win8升级至Win8.1预览版