ImageVerifierCode 换一换
格式:DOCX , 页数:30 ,大小:26.69KB ,
资源ID:12437546      下载积分:8 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bingdoc.com/d-12437546.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(MFC多线程编程.docx)为本站会员(b****8)主动上传,冰点文库仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰点文库(发送邮件至service@bingdoc.com或直接QQ联系客服),我们立即给予删除!

MFC多线程编程.docx

1、MFC多线程编程MFC多线程编程1. 线程的基本概念进程和线程都是操作系统的概念。进程是应用程序的执行实例。每个进程是由私有的虚拟地址空间、代码、数据和其它系统资源组成的。进程在运行过程中创建的资源随着进程的终止而被销毁,所使用的系统资源在进程终止时被释放或关闭。线程是进程内部的一个执行单元。系统创建进程后,实际上就是启动了该进程的主执行线程,它以函数地址的形式(比如main或WinMain函数),将程序的启动点提供给Windows系统。主执行线程终止了,进程也就随之终止。每个进程至少有一个主执行线程,它无需由用户主动创建,而是由系统自动创建的。用户根据需要在应用程序中创建其它线程,多个线程并

2、发地运行于同一个进程中。一个进程中的所有线程都在该进程的虚拟地址空间中,共同使用这些虚拟地址空间、全局变量和系统资源,所以线程间的通讯非常方便,多线程技术的应用也较为广泛。多线程可以实现并行处理,避免了某项任务长时间占用CPU。单CPU运行多线程时,操作系统为每个线程安排一些CPU时间,轮换方式向线程提供时间片,这就给人一种假象,似乎这些线程在同时运行。由此可见,如果两个非常活跃的线程为了抢夺CPU的控制权,在线程切换时会消耗很多CPU资源,反而会降低系统性能。这点要在编程时注意。创建线程共三种方法:(1)WIN32 API的CreateThread()(2)MFC全局函数AfxBeginTh

3、read()(3)CWinThread:CreateThread()2. WIN32 API多线程2.1 WIN32 API多线程函数1. HANDLE CreateThread( /方法一LPSECURITY_ATTRIBUTES lpThreadAttributes,DWORD dwStackSize,LPTHREAD_START_ROUTINE lpStartAddress,LPVOID lpParameter,DWORD dwCreationFlags,LPDWORD lpThreadId);2. DWORD SuspendThread(HANDLE hThread); /挂起指定的线

4、程3. DWORD ResumeThread(HANDLE hThread); /结束线程的挂起状态,执行线程4. VOID ExitThread(DWORD dwExitCode); /主要在线程的执行函数中被调用,终结线程自身的执行5. BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode); /强行终止线程,一般不建议使用:(1)是不安全的,可能会引起系统不稳定;(2)虽然该函数立即终止线程,但并不释放线程所占用的资源。6. BOOL PostThreadMessage(DWORD idThread,UINT Msg,WPARAM w

5、Param,LPARAM lParam); /将一条消息放到指定线程的消息队列中,并且不等到消息被该线程处理时便返回。如果接收消息的线程没有创建消息循环,则该函数执行失败。7. DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds); /功能:在某一线程(创建子线程的线程)中调用,线程暂时挂起,系统监视hHandle(子线程句柄)所指向的对象的状态。返回:如果在挂起的dwMilliseconds毫秒内,线程所等待的对象变为有信号状态,则该函数立即返回;如果超时时间已经到达dwMilliseconds毫秒,但hHandle所

6、指向的对象还没有变成有信号状态,函数照样返回。参数dwMilliseconds有两个特殊值:0和INFINITE。若为0,立即返回;若为INFINITE,则线程一直被挂起,直到hHandle所指向的对象变为有信号状态。2.2 例程MultiThread1使用Win32 API编写多线程。1. 建立基于对话框的工程,在对话框中加入两个按钮和一个编辑框,两个按钮的ID分别是IDC_START、IDC_STOP ,标题分别为“启动”,“停止”,IDC_STOP的属性选中Disabled;编辑框IDC_TIME 的属性选中Read only;2. 在MultiThread1Dlg.h中添加线程函数声明

7、void ThreadFunc();(应在类CMultiThread1Dlg声明外部)3. 在类CMultiThread1Dlg内部添加protected型变量:HANDLE hThread; /线程句柄DWORD ThreadID; /线程ID4. 在MultiThread1Dlg.cpp文件中添加全局变量m_bRun:volatile BOOL m_bRun; /代表线程是否正在运行volatile修饰符:告诉编译器无需对该变量作任何的优化,即无需将它放到一个寄存器中,并且该值可被外部改变。对于多线程引用的全局变量来说,volatile是一个非常重要的修饰符;5. 编写代码:IDC_STA

8、RT的消息函数: void CMultiThread1Dlg:OnStart()/ TODO: Add your control notification handler code herehThread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadFunc,NULL,0,&ThreadID);GetDlgItem(IDC_START)-EnableWindow(FALSE);GetDlgItem(IDC_STOP)-EnableWindow(TRUE);IDC_STOP的消息函数:void CMultiThread1Dlg:OnStop

9、()/ TODO: Add your control notification handler code herem_bRun=FALSE;GetDlgItem(IDC_START)-EnableWindow(TRUE);GetDlgItem(IDC_STOP)-EnableWindow(FALSE);线程函数:void ThreadFunc()CTime time;CString strTime;m_bRun=TRUE; /m_bRun=TRUE,线程一直运行while(m_bRun) time=CTime:GetCurrentTime();strTime=time.Format(%H:%M

10、:%S);:SetDlgItemText(:FindWindow(NULL, LMultiThread1),IDC_TIME,strTime);/:SetDlgItemText(AfxGetApp()-m_pMainWnd-m_hWnd,IDC_TIME,strTime); /也能满足要求/:SetDlgItemText(AfxGetMainWnd()-m_hWnd,IDC_TIME,strTime); /VS2010中画线代码不能获取窗口句柄!Sleep(1000);MultiThread2(1)如何将一个整型参数传送到一个线程;(2)如何等待一个线程完成处理。1. 建立一个基于对话框的工程

11、,在对话框中加入一个编辑框和一个按钮,ID分别是IDC_COUNT,IDC_START ,按钮控件的标题为“开始”;2. 在MultiThread2Dlg.h中添加线程函数声明: void ThreadFunc(int integer);(在类CMultiThread2Dlg声明外部)3. 在类CMultiThread2Dlg内部添加protected型变量:HANDLE hThread; /线程句柄DWORD ThreadID; /线程ID4. 利用ClassWizard为编辑框IDC_COUNT添加int m_nCount;5. 编写代码:IDC_START的消息函数:void CMult

12、iThread2Dlg:OnStart()UpdateData(TRUE);int integer=m_nCount;hThread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadFunc,(VOID*)integer,0,&ThreadID);GetDlgItem(IDC_START)-EnableWindow(FALSE);WaitForSingleObject(hThread, INFINITE);GetDlgItem(IDC_START)-EnableWindow(TRUE);线程函数:void ThreadFunc(int int

13、eger)int i;for(i=0;iEnableWindow(FALSE);WaitForSingleObject(hThread, INFINITE);GetDlgItem(IDC_START)-EnableWindow(TRUE);*/*如果执行上面/* */语句,编译运行,程序停止反应,这是因为线程死锁。因为WaitForSingleObject函数会将主线程挂起(任何消息都得不到处理),而子线程ThreadFunc正在设置进度条,它等主线程将进度条刷新的消息处理完后才会检测通知事件,这样两个线程都在互相等待,死锁发生了,编程时应注意避免。*/在BOOL CMultiThread3D

14、lg:OnInitDialog()中添加代码:/ TODO: Add extra initialization herem_ctrlProgress.SetRange(0,99);m_nMilliSecond=10;UpdateData(FALSE);return TRUE; / return TRUE unless you set the focus to a control线程函数:UINT ThreadFunc(LPVOID lpParam)threadInfo* pInfo=(threadInfo*)lpParam;for(int i=0;inMilliSecond;pInfo-pct

15、rlProgress-SetPos(i);Sleep(nTemp);return 0;MultiThread4测试在Windows下最多可创建线程的数目。1. 建立一个基于对话框的工程,在对话框中加入一个按钮IDC_TEST,标题为“测试”,一个编辑框IDC_COUNT,属性选中Read-only;2. 在MultiThread4Dlg.cpp中添加公共变量volatile BOOL m_bRunFlag = TRUE; /表示是否还能继续创建线程3. 为IDC_COUNT添加int m_nCount;4. 在MultiThread4Dlg.h中添加:DWORD WINAPI threadFu

16、nc(LPVOID threadNum);(在类CMultiThread4Dlg外部)5. 编写代码:IDC_TEST的消息函数:void CMultiThread4Dlg:OnTest()DWORD threadID;GetDlgItem(IDC_TEST)-EnableWindow(FALSE);long nCount=0;while(m_bRunFlag) /不断创建线程,直到再不能创建为止if(NULL = CreateThread(NULL, 0, threadFunc, NULL, 0, &threadID)m_bRunFlag=FALSE;break;elsenCount+;m_

17、nCount=nCount;UpdateData(FALSE);Sleep(5000); /延时5秒,等待所有创建的线程结束GetDlgItem(IDC_TEST)-EnableWindow(TRUE);m_bRunFlag=TRUE;线程函数:DWORD WINAPI threadFunc(LPVOID threadNum)while(m_bRunFlag)Sleep(3000);return 0;3. MFC多线程3.1 基本概念MFC中有两类线程:(1)工作者线程;(2)用户界面线程。二者主要区别:(1)没有消息循环,(2)有自己的消息队列和消息循环。(1)通常用来执行后台计算和维护任务

18、,如冗长的计算,后台打印机打印等。(2)一般用于处理独立于其他线程之外的用户输入,响应用户及系统所产生的事件和消息等。3.1.1 MFC中创建线程1 MFC全局函数AfxBeginThread():它有两种重载形式,分别用于创建工作者线程和用户界面线程: /方法二(1) CWinThread* AfxBeginThread(AFX_THREADPROC pfnThreadProc, /工作者线程函数指针UINT ExecutingFunction(LPVOID pParam);其返回值为0LPVOID pParam,nPriority=THREAD_PRIORITY_NORMAL,UINT n

19、StackSize=0,DWORD dwCreateFlags=0,LPSECURITY_ATTRIBUTES lpSecurityAttrs=NULL);(2) CWinThread* AfxBeginThread(CRuntimeClass* pThreadClass,int nPriority=THREAD_PRIORITY_NORMAL,UINT nStackSize=0,DWORD dwCreateFlags=0,LPSECURITY_ATTRIBUTES lpSecurityAttrs=NULL);2 CWinThread类:(1)创建CWinThread类的一个对象,(2)调用C

20、WinThread:CreateThread()启动线程: /方法三(1) BOOL CWinThread:CreateThread(DWORD dwCreateFlags=0,UINT nStackSize=0,LPSECURITY_ATTRIBUTES lpSecurityAttrs=NULL);(2) virtual BOOL CWinThread:InitInstance(); /用于用户界面线程的初始化;工作者线程一般不用(3) virtual int CWinThread:ExitInstance(); /线程终结前进行一些清理工作;通常用于用户界面线程CWinThread类的用法

21、:A. 创建用户界面线程的步骤:1. 创建类CWinThread的派生类(以CUIThread类为例)2. 重载InitInstance()与ExitInstance():BOOL CUIThread:InitInstance()CFrameWnd* wnd=new CFrameWnd;wnd-Create(NULL,UI Thread Window);wnd-ShowWindow(SW_SHOW);wnd-UpdateWindow();m_pMainWnd=wnd;return TRUE;3. 创建用户界面线程:void CUIThreadDlg:OnButton1()CUIThread*

22、pThread=new CUIThread();pThread-CreateThread();用户界面线程的执行次序与应用程序主线程相同:a) 调用用户界面线程类的InitInstance();b) 若InitInstance()返回TRUE,则调用Run(),运行一个标准的消息循环:线程空闲时调用OnIdle();收到WM_QUIT消息中断线程。c) Run()返回时,MFC调用ExitInstance()清理资源。B. 创建没有界面而有消息循环的线程:(这种方法可以完成一个工作者线程的功能)1. 从CWinThread派生一个新类;2. 在InitInstance()中不创建界面并最后返回

23、FALSE,这表示仅执行InitInstance()中的任务而不执行消息循环。3.1.2 线程间通信线程通信:两个线程之间信息传递的渠道。1. 全局变量由于同一进程的各个线程共享该进程的资源,故线程通信最简单的方法是使用全局变量。对于标准类型的全局变量,建议使用volatile修饰符,它告诉编译器无需对变量作任何优化:无需将它放到寄存器中,且该值可被外部改变。如果线程间传递的信息较复杂,可以定义一个结构体,通过结构体的指针进行信息传递。2. 自定义消息可以在线程函数中向另一线程发送自定义消息来进行线程通信。一个线程向另一线程发送消息是通过操作系统实现的。Windows的消息驱动机制:当一个线程

24、发出一条消息时,Windows首先接收到该消息,然后把该消息转发给目标线程,目标线程必须已经建立了消息循环。3.1.3 线程的同步线程的同步:使隶属于同一进程的各个线程协调一致地工作。为什么要进行线程的同步?虽然多线程会带来诸多好处,但也有问题需要解决:例如,磁盘驱动器是独占性的系统资源,由于线程可以执行进程的任何代码段,且线程的运行是由系统自动调度完成的,具有一定的不确定性,因此就可能出现两个线程同时对磁盘驱动器进行操作的错误。MFC主要提供的四种线程同步对象:A. 临界区(CCriticalSection)B. 互斥量(CMutex)C. 事件(CEvent)D. 信号量(CSemapho

25、re)3.1.3.1 CCriticalSection某一时刻只有一个线程可以拥有CriticalSection对象,该线程可以访问被保护起来的资源或代码段,其他希望进入CriticalSection的线程被挂起,直到该线程放弃CriticalSection为止。这样就保证了在同一时刻不会出现多个线程访问共享资源的情况。其用法如下:1. 定义CCriticalSection类的一个全局对象(以使各个线程均能访问);2. 在需要保护的资源或代码之前,调用CCriticalSection:Lock()获得临界区对象;3. 访问临界区完毕后,调用CCriticalSection:Unlock()来释

26、放临界区。3.1.3.2 CMutexMutex对象与CriticalSection对象很相似,其不同在于:前者可以在进程间使用,而后者只能在同一进程的各线程间使用,但它比前者更节省系统资源,更有效率。3.1.3.3 CEventCEvent支持事件。事件:是一种同步对象,它在一个线程发生某种情况时,唤醒另一线程。例如:在某些网络Apps中,一个线程A负责监听通讯端口,另一线程B负责更新用户数据。通过使用CEvent 类,A可以通知B何时更新用户数据。CEvent对象有两种状态:有信号和无信号。线程监视其CEvent对象的状态,并在相应的时候采取相应的操作。MFC中CEvent有两种类型:自动

27、事件和人工事件。CEvent类默认创建的是前者,它被至少一个线程释放后,自动返回无信号;后者需要调用ReSetEvent()才能设置为无信号。CEvent成员函数:1. CEvent(BOOL bInitiallyOwn=FALSE, /指定事件对象初始化状态:TRUE为有信号BOOL bManualReset=FALSE, /指定要创建的是人工事件还是自动事件:TRUE为人工事件LPCTSTR lpszName=NULL, /一般为NULLLPSECURITY_ATTRIBUTES lpsaAttribute=NULL); /一般为NULL2. BOOL CEvent:SetEvent();/对于人工事件,它将CEvent对象保持为有信号,直到调用ResetEvent()为止;/对于自动事件,它将CEvent对象设置为有信号,CEvent 对象由系统自动重置为无信号。3. BOOL CEvent:ResetEvent();/它将事件设置为无信号,并保持该状态直至调用SetEvent()为止。/自动事件不需要调用它。/一般通过WaitForSingleObject()监视事件状态。3.1.3.4 CSemaphoreCSemaphore提供了访问共享资源线程的计数,它能限制访问共享资源线程的数目。CSemaphore构造函数:CSemaphore (LONG

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2