




已阅读5页,还剩25页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
MFC下的多线程编程,作者:陈帅,2008年7月30日,一、MFC 支持的两种线程:,1. 用户界面线程,通常用于处理用户输入及响应用户生成的事件和消息,并独立地相应正在应用程序其他部分执行的线程产生的消息和时间,并包含一个消息泵(a Message Pump)。用户界面线程包含一个消息处理的循环,以应对各种事件。,对于用户来说工作线程运行在后台。这就使得工作线程特别适合去等待一个事件的发生。,2. 工作线程,工作线程适用于处理那些不要求用户输入并且比较消耗时间的其 他任务(如大规模的重复计算,网络数据的发送与接受)。,注意:,在MFC应用程序中,所有的线程都是由CWinThread对象来表示的; CWinThread是用户接口线程的基类,CWinApp就是CWinThread派生出来的,在编写用户接口线程时,也需要从CWinThread 类派生出自己的线程类; CWinThread同样是工作线程的基类,但在编写工作线程的时候,升值不必刻意地从CWinThread类派生出自己的线程类对象。用户可以调用MFC框架的AfxBeginThread帮助函数,会创建CWinThread对象。 在Win32API中不区分两种线程,它只需要知道线程的起始地址,就可以开始执行线程。,3.创建MFC的工作线程,(1).编程实现控制函数,一个工作线程对应一个控制函数。线程执行的任务都应编写在控制函数之中。编写实现工作线程的控制函数是创建工作线程的第一步。,控制函数的原型声明是:UNIT ControlFunctionName(LPVOID pParam); 其中, UNIT ControlFunctionName:是控制函数的名字,自定。,参数pParam:是一个32位指针值,是启动工作线程时,有调用的AfxBeginThread()函数传递给工作线程的控制函数的。这个值既可以是指向简单数据类型的指针,用来传递int之类的数值,也可是是指向包含了许多参数的结构体或其他对象的指针;甚至可以忽略它。,(2).创建并启动工作线程,在进程的主线程或其他线程中调用AfxBeginThread()函数就可以创建新的线程,并使线程开始运行。,AfxBeginThread()函数是MFC提供的帮助函数,有两个重载版本,区别在于使用的入口参数不同。一个用于创建并启动用户接口线程,一个用于创建并启动工作线程。,要创建并启动工作线程,必须采用如下的调用格式:,CWinThread* AfxBeginThread(,AFX_THREADPROC pfnThreadProc,LPVOID pParam, int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );,参数pfnThreadProc:是一个指向工作线程的控制函数的指针,即控制函数的地址。创建工作线程是必须指定将在此线程内部运行的控制函数。,参数pParam:是一个指向某种类型的数据结构指针,执行本函数时,将把这个指针进一步传递给此线程的控制函数,使之成为线程控制函数的入口参数。,参数nPriority:通常设为0。,参数nStackSize:通常设为0。,参数dwCreateFlags :通常设为0。,参数dwCreateFlags :通常设为0。,参数lpSecurityAttrs:通常设为NULL。,(3)创建工作线程的例子,struct int n; double* pD; myData; myData ss;/定义了该类型的变量,对该变量的初始化的代码省略了 UNIT MyCalcFunc(LPVOID pParam) /如果入口函数为空指针,终止线程 if( pParam=NULL) AfxEndThread(MY_NULL_POINTTER_ERROR); int n=pParam-n; /数组元素个数; double* pD=pParam-pD; /指向数组的第一个元素; double sum=0; /数组元素之和; for(int i=0; in; i+) sum+=pDi;/数组之和; CString bb; bb.Format(“数组的和是:%d“,sum);/格式化显示字符串; AfxMessageBox(bb); /显示结果; return 0; ,(i) 编程实现线程控制函数,(2)在程序进程的主线程中调用AfxBeginThread()函数来创建并启动运行这个线程。将控制函数名和结构变量的地址作为参数来传递,其他的参数省略,表示使用默认值。,AfxBeginThread(MyCalcFunc,一旦调用了此函数,线程就被创建,并开始执行线程函数。当数据的计算完成时,函数将停止运行,线程拥有的堆栈和其它的资源都将释放。CWinThread对象将被删除。,3. 创建并启动用户界面线程,创建并启动用户界面线程一般要经过3个步骤: 第一步是从CWinThread类派生出自己的线程类; 第二步是改造这个线程类,使它能够完成用户所希望的工作; 第三步是创建并启动用户界面线程。,(1)从CWinThread类派生出自己的线程类,要创建一个MFC的用户界面线程,所要做的第一件事就是从CWinThread类派生出自己的线程类,一般借助ClassWizard来做这项工作。,(2)改造自己的线程类,(i)在这个线程类的.h头文件中,使用DECLARE_DYNCRATE宏来声明这个类;在用户线程类的.cpp实现文件中,使用IMPLEMENT_DYNCREATE宏来实现这个类。,前者的调用格式是: DECLARE_DYNCRATE(class_name),其中class_name中是实际的类名。,(ii)如果在一个类中宣布使用了DECLARE_DYNCRATE宏,那么就必须在这个类的.cpp实现文件中,使用IMPLEMENT_DYNCREATE宏。它的调用格式是: IMPLEMENT_DYNCREATE(class_name, base_class_name) 参数是实际的线程类名和它的基类名。,(iii)这个线程类必须重载它的基类(CWinThread类)的某些成员函数,如该类的InitInstance()成员函数;对于基类的其它成员函数,可以有选择的重载,也可以使用缺省函数。,创建用户界面线程时相关成员函数的重载,(vi) 创建新的用户界面窗口类,如窗口、对话框,并添加所需要的用户界面控件,然后建立新建的线程类与这些用户界面窗口类的联系。,(v)利用类向导,为新建的线程类添加控件成员变量,添加响应消息的成员函数,为它们编写实现的代码,经过以上步骤的改造,用户的线程类已经具备了完成用户任务的能力.,(3)创建并启用用户界面线程,要创建并启动用户界面线程,可以使用MFC提供的AfxBeginThread()函数的另一个版本,其格式是:,CWinThread* AfxBeginThread( CRuntimeClass* pThreadClass, int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );,参数pThreadClass:是一个指向CRuntimeClass类对象的指针,该类是从CWinThread类继承的。用户界面线程运行时类就在第一步骤从CWinThread派生的线程类,本参数就指向它,在实际调用时,一般使用RUNTIME_CLASS 宏将线程类指针转化为指向CRuntimeClass对象的指针。 其他的参数这和创建启动工作线程时一样。,4.终止线程,(1) 正常终止线程,VOID PostQuitMessage()函数的调用格式是: VOID PostQuitMessage(int nExitCode); 参数nExitCode是一个整数型值,指定一个应用程序的终止代码。,PostQuitMessage()函数发送一个WM_QUIT消息到线程的消息队列,并立即返回,没有返回值。函数只是简单地告诉系统,这个线程要求终止。当线程从它的消息队列收到一个WM_QUIT消息时,会退出它的消息循环,并将控制权返回给系统,同时把WM_QUIT消息的wParam参数中的终止代码也返回给系统,线程也就终止了。,(2)提前终止线程,要想在线程尚未完成它的工作时提前终止线程,只需从线程内调用AfxEndThread函数,就可以强迫线程终止。此函数的调用格式是:,Void AfxEndThread(UNIT nExitCode);,参数nExitCode指定了线程的终止代码。,执行此函数将停止函数所在线程的执行,撤销该线程的堆栈,解除所有绑定到此线程动态链接库DLLs,并从内存中删除此线程。,(3)终止线程另一种方法:,使用Win32 API 提供的TerminateThread()函数,也可以用来终止一个正在运行的线程,但是它产生的后果是不可预料的,一般仅用来终止堆栈中的死线程,而且此函数本身不做任何内存的清除工作。,5.MFC下多线程的同步机制,(1)基本概念: 在程序中使用多线程时,一般很少有多个线程能在其生命期内进行完全独立的操作。更多的情况是一些线程进行某些处理操作,而其他的线程必须对其处理结果进行了解。正常情况下对这种处理结果的了解应当在其处理任务完成后进行。,下述对象是用来支持同步的: 1)信号量 2)互斥锁 3)临界区 4)事件,1)信号量,为了限制使用共享资源的线程数目,我们应该使用信号量。信号量是一个内核对象。它存储了一个计数器变量来跟踪使用共享资源的线程数目。例如,下面代码使用CSemaphore类创建了一个信号量对象,它确保在给定的时间间隔内(由构造函数第一个参数指定)最多只有5个线程能使用共享资源。还假定初始时没有线程获得资源:,CSemaphore g_Sem(5, 5);,一旦线程访问共享资源,信号量的计数器就减1.若变为0,则接下来对资源的访问会被拒绝,直到有一个持有资源的线程离开(也就是说释放了信号量)。我们可以如下使用:,/ Try to use the shared resource :WaitForSingleObject(g_Sem, INFINITE); / Now the users counter of the semaphore has decremented by one / Use the shared resource / After we done, let other threads use the resource :ReleaseSemaphore(g_Sem, 1, NULL); / Now the users counter of the semaphore has incremented by one,互斥锁设计为对同步访问共享资源进行保护。互斥锁在内核中实现,因此需要进入内核模式操纵它们。互斥锁不仅能在不同线程之间,也可以在不同进程之间进程同步。要跨进程使用,则互斥锁应该是有名的。MFC中使用CMutex类来操纵互斥锁。可以如下方式使用:,2)互斥锁,CSingleLock singleLock( ,3)临界区,临界区(Critical Section)是一段独占对某些共享资源访问的代码,在任意时刻只允许一个线程对共享资源进行访问。如果有多个线程试图同时访问临界区,那么在有一个线程进入后其他所有试图访问此临界区的线程将被挂起,并一直持续到进入临界区的线程离开。临界区在被释放后,其他线程可以继续抢占,并以此达到用原子方式操作共享资源的目的。,临界区在使用时以CRITICAL_SECTION结构对象保护共享资源,并分别用EnterCriticalSection()和LeaveCriticalSection()函数去标识和释放一个临界区。所用到的CRITICAL_SECTION结构对象必须经过InitializeCriticalSection()的初始化后才能使用,而且必须确保所有线程中的任何试图访问此共享资源的代码都处在此临界区的保护之下。否则临界区将不会起到应有的作用,共享资源依然有被破坏的可能。,MFC为临界区提供有一个CCriticalSection类,使用该类进行线程同步处理是非常简单的,只需在线程函数中用CCriticalSection类成员函数Lock()和UnLock()标定出被保护代码片段即可。,如:/ MFC临界区类对象 CCriticalSection g_clsCriticalSection; / 共享资源 char g_cArray10; UINT ThreadProc20(LPVOID pParam) / 进入临界区 g_clsCriticalSection.Lock(); / 对共享资源进行写入操作 for (int i = 0; i 10; i+) g_cArrayi = a; Sleep(1); / 离开临界区 g_clsCriticalSection.Unlock(); return 0; ,4)事件,一般来说,事件用于这样的情形下:当指定的动作发生后,一个线程(或多个线程)才开始执行其任务。例如,一个线程可能等待必需的数据收集完后才开始将其保存到硬盘上。,有两种事件:手动重置型和自动重置型。通过使用事件,我们可以轻松地通知另一个线程特定的动作已经发生了。对于手动重置型事件,线程使用它通知多个线程特定动作已经发生,而对于自动重置型事件,线程使用它只可以通知一个线程。,在MFC中,CEvent类封装了事件对象(若在win32中,它是用一个HANDLE来表示的)。CEvent的构造函数运行我们选择创建手动重置型和自动重置型事件。默认的创建类型是自动重置型事件。为了通知正在等待的线程,我们可以调用CEvent:SetEvent方法,这个方法将会让事件进入已通知状态。若事件是手动重置型,则事件会保持已通知状态,直到对应的CEvent:ResetEvent被调用,这个方法将使得事件进入未通知状态。这个特性使得一个线程可以通过一个SetEvent调用去通知多个线程。若事件是自动重置型,则所有正在等待的线程中只有一个线程会接收到通知。当那个线程接收到通知后,事件会自动进入未通知状态。,CEvent g_eventStart; UINT ThreadProc1(LPVOID pParam) :WaitForSingleObject(g_eventStart, INFINITE); return 0; UINT ThreadProc2(LPVOID pParam) :WaitForSingleObject(g_eventStart, INFINITE); return 0; ,在这个例子中,一个全局的
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年黑龙江建筑职业技术学院高职单招职业技能测试近5年常考版参考题库含答案解析
- 2025年重庆电信职业学院高职单招职业适应性测试历年(2019-2024年)真题考点试卷含答案解析
- 2025年鄂州职业大学高职单招语文2019-2024历年真题考点试卷含答案解析
- 中小学教师资格考试定期自测安排试题及答案
- 2025年执业医师考试研究资源试题及答案
- 会计师考试资料共享的重要性与实施措施试题及答案
- 2025年初级会计师考试会计职业发展试题及答案
- 2025年公共卫生执业医师考试健康管理系统试题及答案
- 健康管理师考试中情境分析题应对方式试题及答案
- 2025年二级计算机考试各个学科知识点总结试题及答案
- 2025年内蒙古中考一模英语试题(原卷版+解析版)
- 银行案件防控课件
- 2025年江苏省安全员B证考试题库附答案
- 科级试用期满工作总结(4篇)
- 历史-安徽省蚌埠市2025届高三年级第二次教学质量检查考试(蚌埠二模)试题和答案
- 2025年从大模型、智能体到复杂AI应用系统的构建报告-以产业大脑为例-浙江大学(肖俊)
- 2025年浙江省金华市中考一模数学模拟试题(含答案)
- 2024年国家发展和改革委员会直属单位招聘考试真题
- 2025年河南省商丘市柘城县中考一模化学试题(原卷版+解析版)
- 《中国古代神话》课件
- 外研版(2025新版)七年级下册英语期中复习:Unit 1~3+期中共4套学情调研测试卷(含答案)
评论
0/150
提交评论