已阅读5页,还剩10页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
MFC多线程编程实例一般情况下,调用AfxBeginThread()来一次性地创建并启动一个线程,但是也可以通过两步法来创建线程:首先创建CWinThread类的一个对象,然后调用该对象的成员函数CreateThread()来启动该线程。virtual BOOL CWinThread:InitInstance();重载该函数以控制用户界面线程实例的初始化。初始化成功则返回非0值,否则返回0。用户界面线程经常重载该函数,工作者线程一般不使用InitInstance()。virtual int CWinThread:ExitInstance();在线程终结前重载该函数进行一些必要的清理工作。该函数返回线程的退出码,0表示执行成功,非0值用来标识各种错误。同InitInstance()成员函数一样,该函数也只适用于用户界面线程。步骤/方法1.MFC多线程编程实例在Visual C+6.0编程环境中,我们既可以编写C风格的32位Win32应用程序,也可以利用MFC类库编写C+风格的应用程序,二者各有其优缺点。基于Win32的应用程序执行代码小巧,运行效率高,但要求程序员编写的代码较多,且需要管理系统提供给程序的所有资源;而基于MFC类库的应用程序可以快速建立起应用程序,类库为程序员提供了大量的封装类,而且DeveloperStudio为程序员提供了一些工具来管理用户源程序,其缺点是类库代码很庞大。由于使用类库所带来的快速、简捷和功能强大等优越性,因此除非有特殊的需要,否则VisualC+推荐使用MFC类库进行程序开发。我们知道,MFC中的线程分为两种:用户界面线程和工作者线程。我们将分别举例说明。用 MFC 类库编程实现工作者线程例程5 MultiThread5为了与Win32 API对照,我们使用MFC 类库编程实现例程3 MultiThread3。建立一个基于对话框的工程MultiThread5,在对话框IDD_MULTITHREAD5_DIALOG中加入一个编辑框IDC_MILLISECOND,一个按钮IDC_START,标题为“开始”,一个进度条IDC_PROGRESS1;打开ClassWizard,为编辑框IDC_MILLISECOND添加int型变量m_nMilliSecond,为进度条IDC_PROGRESS1添加CProgressCtrl型变量m_ctrlProgress;在MultiThread5Dlg.h文件中添加一个结构的定义: struct threadInfoUINT nMilliSecond;CProgressCtrl* pctrlProgress; 2.线程函数的声明:UINT ThreadFunc(LPVOID lpParam);注意,二者应在类CMultiThread5Dlg的外部。在类CMultiThread5Dlg内部添加protected型变量:CWinThread* pThread;在MultiThread5Dlg.cpp文件中进行如下操作:定义公共变量:threadInfo Info;双击按钮IDC_START,添加相应消息处理函数:void CMultiThread5Dlg:OnStart()/ TODO: Add your control notification handler code hereUpdateData(TRUE);Info.nMilliSecond=m_nMilliSecond;Info.pctrlProgress=&m_ctrlProgress;pThread=AfxBeginThread(ThreadFunc,&Info);在函数BOOL CMultiThread3Dlg: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-pctrlProgress-SetPos(i);Sleep(nTemp);return 0;用 MFC 类库编程实现用户界面线程3.创建用户界面线程的步骤:使用ClassWizard创建类CWinThread的派生类(以CUIThread类为例) class CUIThread : publicCWinThreadDECLARE_DYNCREATE(CUIThread)protected:CUIThread(); / protected constructor used by dynamic creation/ Attributespublic:/ Operationspublic:/ Overrides/ ClassWizard generated virtual function overrides/AFX_VIRTUAL(CUIThread)public:virtual BOOL InitInstance();virtual int ExitInstance();/AFX_VIRTUAL/ Implementationprotected:virtual CUIThread();/ Generated message map functions/AFX_MSG(CUIThread)/ NOTE - the ClassWizard will add and remove member functions here./AFX_MSGDECLARE_MESSAGE_MAP();重载函数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;创建新的用户界面线程 void CUIThreadDlg:OnButton1()CUIThread* pThread=new CUIThread();pThread-CreateThread(); 4.请注意以下两点:A、在UIThreadDlg.cpp的开头加入语句: #include UIThread.hB、把UIThread.h中类CUIThread()的构造函数的特性由 protected 改为 public。用户界面线程的执行次序与应用程序主线程相同,首先调用用户界面线程类的InitInstance()函数,如果返回TRUE,继续调用线程的Run()函数,该函数的作用是运行一个标准的消息循环,并且当收到WM_QUIT消息后中断,在消息循环过程中,Run()函数检测到线程空闲时(没有消息),也将调用OnIdle()函数,最后Run()函数返回,MFC调用ExitInstance()函数清理资源。你可以创建一个没有界面而有消息循环的线程,例如:你可以从CWinThread派生一个新类,在InitInstance函数中完成某项任务并返回FALSE,这表示仅执行InitInstance函数中的任务而不执行消息循环,你可以通过这种方法,完成一个工作者线程的功能。例程6 MultiThread6建立一个基于对话框的工程MultiThread6,在对话框IDD_MULTITHREAD6_DIALOG中加入一个按钮IDC_UI_THREAD,标题为“用户界面线程”右击工程并选中“New Class”为工程添加基类为CWinThread派生线程类CUIThread。给工程添加新对话框IDD_UITHREADDLG,标题为“线程对话框”。5.为对话框IDD_UITHREADDLG创建一个基于CDialog的类CUIThreadDlg。使用ClassWizard为CUIThreadDlg类添加WM_LBUTTONDOWN消息的处理函数OnLButtonDown,如下:void CUIThreadDlg:OnLButtonDown(UINT nFlags, CPoint point)AfxMessageBox(You Clicked The Left Button!);CDialog:OnLButtonDown(nFlags, point);在UIThread.h中添加 #include UIThreadDlg.h并在CUIThread类中添加protected变量CUIThread m_dlg: class CUIThread : public CWinThreadDECLARE_DYNCREATE(CUIThread)protected:CUIThread(); / protected constructor used by dynamic creation/ Attributespublic:/ Operationspublic:/ Overrides/ ClassWizard generated virtual function overrides/AFX_VIRTUAL(CUIThread)public:virtual BOOL InitInstance();virtual int ExitInstance();/AFX_VIRTUAL/ Implementationprotected:CUIThreadDlg m_dlg;virtual CUIThread();/ Generated message map functions/AFX_MSG(CUIThread)/ NOTE - the ClassWizard will add and remove member functions here./AFX_MSGDECLARE_MESSAGE_MAP();分别重载InitInstance()函数和ExitInstance()函数: BOOL CUIThread:InitInstance()m_dlg.Create(IDD_UITHREADDLG);m_dlg.ShowWindow(SW_SHOW);m_pMainWnd=&m_dlg;return TRUE;int CUIThread:ExitInstance()m_dlg.DestroyWindow();return CWinThread:ExitInstance();双击按钮IDC_UI_THREAD,添加消息响应函数: void CMultiThread6Dlg:OnUiThread()CWinThread *pThread=AfxBeginThread(RUNTIME_CLASS(CUIThread);并在MultiThread6Dlg.cpp的开头添加: #include UIThread.h好了,编译并运行程序吧。每单击一次“用户界面线程”按钮,都会弹出一个线程对话框,在任何一个线程对话框内按下鼠标左建弹出一个消息框。.WaitForSingleObject 的用法 DWORDWaitForSingleObject( HANDLE hHandle, DWORD dwMilliseconds );参数 hHandle 是一个事件的句柄,第二个参数 dwMilliseconds 是时间间隔。如果时间是有信号状态返回 WAIT_OBJECT_0 ,如果时间超过 dwMilliseconds 值但时间事件还是无信号状态则返回 WAIT_TIMEOUT 。hHandle 可以是下列对象的句柄:Change notification Console input Event Job Memory resource notification Mutex Process Semaphore Thread Waitable timer WaitForSingleObject 函数用来检测 hHandle 事件的信号状态,当函数的执行时间超过 dwMilliseconds 就返回,但如果参数 dwMilliseconds 为 INFINITE 时函数将直到相应时间事件变成有信号状态才返回,否则就一直等待下去,直到 WaitForSingleObject 有返回直才执行后面的代码。在这里举个例子: 先创建一个全局 Event 对象 g_event: CEvent g_event; 在程序中可以通过调用 CEvent:SetEvent 设置事件为有信号状态。 下面是一个线程函数 MyThreadPro() UINT CFlushDlg:MyThreadProc( LPVOID pParam ) WaitForSingleObject(g_event,INFINITE); For(;) . return 0; 在这个线程函数中只有设置 g_event 为有信号状态时才执行下面的 for 循环,因为 g_event 是全局变量,所以我们可以在别的线程中通过 g_event. SetEvent 控制这个线程。还有一种用法就是我们可以通过 WaitForSingleObject 函数来间隔的执行一个线程函数的函数体 UINT CFlushDlg:MyThreadProc( LPVOID pParam ) while(WaitForSingleObject(g_event,MT_INTERVAL)!=WAIT_OBJECT_0) return 0; 在这个线程函数中可以可以通过设置 MT_INTERVAL 来控制这个线程的函数体多久执行一次,当事件为无信号状态时函数体隔 MT_INTERVAL 执行一次,当设置事件为有信号状态时,线程就执行完毕了。 2. WaitForSingleObject当指定的对象处于有信号状态或者等待时间结束的状态时,此函数返回。DWORD WaitForSingleObject( HANDLE hHandle, DWORD dwMilliseconds);参数:hHandle:指定对象或事件的句柄;dwMilliseconds: 等待时间,以毫妙为单位,当超过等待时间时,此函数将返回。如果该参数设置为0,则该函数立即返回,如果设置为INFINITE,则该函数直到有信号才返回。返回值:如果此函数成功,该函数的返回之标识了引起该函数返回的事件。返回值如下: WAIT_ABANDONED(0x00000080L) 指定的对象是一个互斥对象,该对象没有被拥有该对象的线程在线程结束前释放。互斥对象的所有权被同意授予调用该函数的线程。互斥对象被设置成为无信号状态。 WAIT_OBJECT_0 (0x00000000L) 指定的对象出有有信号状态。 WAIT_TIMEOUT (0x00000102L) 超过等待时间,指定的对象处于无信号状态如果失败,返回 WAIT_FAILED;备注:此函数检查指定的对象或事件的状态,如果该对象处于无信号状态,则调用线程处于等待状态,此时该线程不消耗CPU时间,该函数可以等待如下对象: Change notification Console input Event Job Memory resource notification Mutex Process Semaphore Thread Waitable timer 需求:Client Requires Windows XP, Windows 2000 Professional, Windows NT Workstation, Windows Me, Windows 98, or Windows 95. Server Requires Windows Server 2003, Windows 2000 Server, or Windows NT Server. Header Declared in Winbase.h; include Windows.h. Library Link to Kernel32.lib. DLL Requires Kernel32.dll. 程序举例:1、创建对话框应用程序,项目名称为MyTestThread2、添加按钮,命名为启动和停止,在对话框中增加编辑框,ID为IDC_TIME,3、增加成员变量,HANDLE m_hThread2,此为线程的句柄;4、定义全局变量,用来控制线程的运行与否; volatile BOOL m_ThreadRun2;5、增加全局事件对象,用来监控线程,控制线程是否运行。 CEvent event; 注意:4、5定义的对象,必须在.cpp文件中定义;6、声明回调函数。回调函数必须是全局函数或静态函数。声明方式如下:void ThreadFunc1(LPVOID pParam);void ThreadFunc2(LPVOID pParam); 回调函数的实现如下:void ThreadFunc1(LPVOID pParam) CTime time; CString strTime; event.ResetEvent(); m_ThreadRun0 = true; m_ThreadRun1 = true; DWORD ThreadID = :GetCurrentThreadId(); while(m_ThreadRun0) time = CTime:GetCurrentTime(); strTime = time.Format(%H:%M:%S); CMyTestThreadDlg* pDlg = (CMyTestThreadDlg*)pParam; pDlg-SetDlgItemText(IDC_TIME,strTime); Sleep(1000); void ThreadFunc2(LPVOID pParam) CTime time; CString strTime; DWORD ThreadID = :GetCurrentThreadId();/event为有信号状态,则下边的函数执行后,该线程则开始运行,如果event为无信号状态,则下边的函数执行/后,该线程处于等待状态,直到有信号才开始运行; :WaitForSingleObject(event,INFINITE); while(m_ThreadRun1) time = CTime:GetCurrentTime(); strTime = time.Format(%H:%M:%S); CMyTestThreadDlg* pDlg = (CMyTestThreadDlg*)pParam; pDlg-SetDlgItemText(IDC_TIME,OK); Sleep(1000); :WaitForSingleObject(event,INFINITE); 7、定义保存线程ID的成员变量:DWORD m_ThreadID2;8、对启动和停止按钮增加消息响应函数,如下:void CMyTestThreadDlg:OnBnClickedOk() / TODO: 在此添加控件通知处理程序代码 m_hThread0 = :CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadFunc1,this,0,&m_ThreadID0); m_hThread1 = :CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadFunc2,this,0,&m_ThreadID1); GetDlgItem(IDC_BUTTON1)-EnableWindow(false); GetDlgItem(IDC_BUTTON2)-EnableWindow(true);void CMyTestThreadDlg:OnBnClickedCancel() m_ThreadRun0 = false; event.SetEvent(); GetDlgItem(IDC_BUTTON1)-EnableWindow(true); GetDlgItem(IDC_BUTTON2)-EnableWindow(false);临界区临界区是一种最简单的同步对象,它只可以在同一进程内部使用。它的作用是保证只有一个线程可以申请到该对象void InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection );产生临界区void DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection );删除临界区void EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection ); 进入临界区,相当于申请加锁,如果该临界区正被其他线程使用则该函数会等待到其他线程释放bool TryEnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection );进入临界区,相当于申请加锁,和EnterCriticalSection不同如果该临界区正被其他线程使用则该函数会立即返回FALSE,而不会等待VOID LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection ); 退出临界区,相当于申请解锁下面的示范代码演示了如何使用临界区来进行数据同步处理:/全局变量int iCounter=0;CRITICAL_SECTION criCounter;DWORD threadA(void* pD) int iID=(int)pD; for(int i=0;im_hThread; hThread1=pT2-m_hThread; hThread2=pT3-m_hThread; /等待线程结束 /至于 WaitForMultipleObjects 的用法后面会讲到。 WaitForMultipleObjects(3,hThread,TRUE,INFINITE); /删除临界区 DeleteCriticalSection(&criCounter); printf(novern);互斥量互斥量与临界区的作用非常相似,但互斥量是可以命名的,也就是说它可以跨越进程使用。所以创建互斥量需要的资源更多,所以如果只为了在进程内部是用的话使用临界区会带来速度上的优势并能够减少资源占用量。因为互斥量是跨进程的互斥量一旦被创建,就可以通过名字打开它创建互斥量:HANDLE CreateMutex( LPSECURITY_ATTRIBUTES lpMutexAttributes, / 安全信息 BOOL bInitialOwner, / 最初状态, /如果设置为真,则表示创建它的线程直接拥有了该互斥量,而不需要再申请 LPCTSTR lpName / 名字,可以为NULL,但这样一来就不能被其他线程/进程打开);打开一个存在的互斥量:HANDLE OpenMutex( DWORD dwDesiredAccess, / 存取方式 BOOL bInheritHandle, / 是否可以被继承 LPCTSTR lpName / 名字);释放互斥量的使用权,但要求调用该函数的线程拥有该互斥量的使用权:BOOL ReleaseMutex( /作用如同LeaveCriticalSection HANDLE hMutex / 句柄);关闭互斥量:BOOL CloseHandle( HANDLE hObject / 句柄);对于互斥量来讲如果正在被使用则为无信号状态,被释放后变为有信号状态。当等待成功后 WaitForSingleObject 函数会将互斥量置为无信号状态,这样其他的线程就不能获得使用权而需要继续等待。WaitForSingleObject 函数还进行排队功能,保证先提出等待请求的线程先获得对象的使用权int iCounter=0;DWORD threadA(void* pD) int iID=(int)pD; /在内部重新打开 HANDLE hCounterIn=OpenMutex(MUTEX_ALL_ACCESS,FALSE,sam sp 44); for(int i=0;im_hThread; hThread1=pT2-m_hThread; hThread2=pT3-m_hThread; /等待线程结束 WaitForMultipleObjects(3,hThread,TRUE,INFINITE); /关闭句柄 CloseHandle(hCounter); WaitForSingleObject 这个函数可以作用于:MutexEventSemaphoreJobProcessThreadWaitable timerConsole input互斥量(Mutex),信号灯(Semaphore),事件(Event)都可以被跨越进程使用来进行同步数据操作,而其他的对象与数据同步操作无关,但对于进程和线程来讲,如果进程和线程在运行状态则为无信号状态,在退出后为有信号状态。所以我们可以使用 WaitForSingleObject 来等待进程和线程退出。(至于信号灯,事件的用法我们接下来会讲)我们在前面的例子中使用了 WaitForMultipleObjects 函数,这个函数的作用与 WaitForSingleObject 类似但从名字上我们可以看出,WaitForMultipleObjects 将用于等待多个对象变为有信号状态,函数原型如下:DWORD WaitForMultipleObjects( DWORD nCount, / 等待的对象数量 CONST HANDLE *lpHandles, / 对象句柄数组指针 BOOL fWaitAll, / 等待方式, /为TRUE表示等待全部对象都变为有信号状态才返回,为FALSE表示任何一个对象变为有信号状态则返回 DWORD dwMilliseconds / 超时设置,以ms为单位,如果为INFINITE表示无限期的等待);dwMilliseconds 的范围从 0 - 0x7fffffff 或者 INFINITE - 0xffffffff返回值意义:WAIT_OBJECT_0 到 (WAIT_OBJECT_0 + nCount 1):当 fWaitAll 为 TRUE 时表示所有对象变为有信号状态,当 fWaitAll 为 FALSE 时使用返回值减去 WAIT_OBJECT_0 得到变为有信号状态的对象在数组中的下标。WAIT_ABANDONED_0 到 (WAIT_ABANDONED_0 + nCount 1):当 fWaitAll 为 TRUE 时表示所有对象变为有信号状态,当 fWaitAll 为 FALSE 时表示对象中有一个对象为互斥量,该互斥量因为被关闭而成为有信号状态,使用返回值减去 WAIT_OBJECT_0 得到变为有信号状态的对象在数组中的下标。WAIT_TIMEOUT:表示超过规定时间。信号灯信号灯有一个初始值,表示有多少进程/线程可以进入,当信号灯的值大于 0 时为有信号状态,小于等于 0 时为无信号状态,所以可以利用 WaitForSingleObject 进行等待,当 WaitForSingleObject 等待成功后信号灯的值会被减少 1,直到释放时信号灯会被增加 1。用于信号灯操作的 API 函数有下面这些:创建信号灯:HANDLE CreateSemaphore( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, / 安全属性,NULL表示使用默认的安全描述 LONG lInitialCount, / 初始值 LONG lMaximumCount, / 最大值 LPCTSTR lpName / 名字);打开信号灯:HANDLE OpenSemaphore( DWORD dwDesiredAccess, / 存取方式 BOOL bInheritHandle, / 是否能被继承 LPCTSTR lpName / 名字);释放信号灯:BOOL ReleaseSemaphore( HANDLE hSemaphore, / 句柄 LONG lReleaseCount, / 释放数,让信号灯值增加数 LPLONG lpPreviousCount / 用来得到释放前信号灯的值,可以为NULL);关闭信号灯:BOOL CloseHandle( HANDLE hObject / 句柄);DWORD threadA(void* pD) int iID=(int)pD; /在内部重新打开 HANDLE hCounterIn
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年石家庄辅警协警招聘考试备考题库及参考答案详解1套
- 2025年达州辅警协警招聘考试真题附答案详解(基础题)
- 2025年镇江辅警招聘考试真题带答案详解
- 2025年黑龙江辅警协警招聘考试备考题库及完整答案详解一套
- 2025年阿里辅警协警招聘考试真题及答案详解(典优)
- 2025年韶关辅警招聘考试真题含答案详解(综合题)
- 2025年白山辅警招聘考试真题附答案详解(巩固)
- 2025年西宁辅警协警招聘考试真题及1套参考答案详解
- 2025年黑龙江辅警招聘考试题库附答案详解(精练)
- 2025年青岛辅警协警招聘考试备考题库及完整答案详解1套
- 船舶机工基础技能培训手册
- 手足显微外科一科一品
- 国际高中英文版数学试卷
- 健康体检报告解读
- 安全培训-重大事故隐患判定标准-专家版
- 学堂在线 工程伦理 章节测试答案
- 2024-2025学年湖北省武汉市部分学校高一上学期期中调研考试数学试卷(解析版)
- 2025年公共艺术课程考试试题及答案汇整
- 狂犬门诊疫苗管理制度
- 不同抑酸剂四联方案与改良伴同方案根除幽门螺杆菌的疗效对比探究
- 乙状结肠癌术后护理查房
评论
0/150
提交评论