1、第9章 多线程 内容摘要:1.创建线程 2.实现线程间通信 3.利用线程同步技术保障数据安全 返回目录第1页,共41页。9.1 创建线程创建线程 学习目标调用AfxBeginThread()来启动线程。返回第9章第2页,共41页。程序的实现步骤1.利用AppWizard生成程序框架(ThreadTest)2.编辑资源 3.添加菜单响应函数 4.编写线程函数 5.在视中输出信息第3页,共41页。步骤1 生成程序框架(MenuTest)1.项目名称:ThreadTest 2.选择单文档界面应用程序(Single document)第4页,共41页。步骤2 编辑资源1.编辑菜单资源 线程 Pop-u
2、p 选中 Caption ID 启动线程 ID_THREAD_START 其他任务 ID_TREEAD_OTHER2.编辑对话框资源 IDD_DIALOG_THRAED CDlgThread 第5页,共41页。步骤3 添加菜单响应函数 Class Name Objects IDs Messages1.CThreadTestView ID_THREAD_START COMMAND2.CThreadTestView ID_TREEAD_OTHER COMMAND第6页,共41页。步骤4 编写线程函数 1.在视类中添加两个成员变量。并在构造函数中初始化class CThreadTestView:pu
3、blic CView public:CString m_strMessage;int m_iTime;CThreadTestView:CThreadTestView()m_strMessage=没有线程启动;m_iTime=0;第7页,共41页。步骤5 在视中输出信息void CThreadTestView:OnDraw(CDC*pDC)CThreadTestDoc*pDoc=GetDocument();ASSERT_VALID(pDoc);/TODO:add draw code for native data here char chNumber6;itoa(m_iTime,chNumber
4、,10);pDC-TextOut(30,30,m_strMessage);pDC-TextOut(30,50,chNumber);第8页,共41页。2.线程函数UINT TreadProc(LPVOID param)CThreadTestApp*pApp=(CThreadTestApp*)AfxGetApp();CMainFrame*pMainFrame=(CMainFrame*)pApp-GetMainWnd();CThreadTestView*pView=(CThreadTestView*)pMainFrame-GetActiveView();pView-m_strMessage=启动了一
5、个线程!启动了一个线程!;while(pView-m_iTime m_iTime+;pView-Invalidate();pView-m_iTime=0;pView-m_strMessage=线程结束!线程结束!;return 0;第9页,共41页。基本知识1.CWnd类的GetSafeHwnd();返回窗口句柄2.AfxBeginThread();3.线程函数(返回UINT)4.CMainFrame:GetActiveView();5.:Sleep(1000);第10页,共41页。9.2 线程间通信 学习目标1.使用全局变量实现线程间通信 2.使用消息实现线程间通信3.使用CEvent类实现
6、线程间通信 返回第9章第11页,共41页。1.使用全局变量实现线程间通信在上一节程序的基础上,进行以下操作:(1)在“线程”菜单中添加菜单项:“终止线程”,ID_THREAD_STOP。(2)在ThreadTestView.cpp文件中添加一个全局变量threadController,用来控制线程是否继续。添加方法是在ThreadTestView.cpp的最上面,在endif下面添加下面的语句:volatile int threadController;(3)在视类中为“停止线程”添加消息处理函数OnThreadStop()。void CThreadTestView:OnThreadStop(
7、)threadController=0;第12页,共41页。1.使用全局变量实现线程间通信4修改OnThreadStart()函数,代码如下所示:void CThreadView:OnStartthread()threadController=1;HWND hWnd=GetSafeHwnd();AfxBeginThread(TreadProc,hWnd,THREAD_PRIORITY_NORMAL);5修改ThreadProc()函数的代码,代码如下:第13页,共41页。UINT TreadProc(LPVOID param)CThreadTestApp*pApp=(CThreadTestAp
8、p*)AfxGetApp();CMainFrame*pMainFrame=(CMainFrame*)pApp-GetMainWnd();CThreadTestView*pView=(CThreadTestView*)pMainFrame-GetActiveView();pView-m_strMessage=启动了一个线程!;while(threadController):Sleep(1000);pView-m_iTime+;pView-Invalidate();pView-m_iTime=0;pView-m_strMessage=线程结束!;return 0;第14页,共41页。2.使用消息实
9、现线程间通信(1)在ThreadTestView.h文件中定义消息:const WM_THREAD_SENDMESS=WM_USER+20;(2)在ThreadTestView.h文件中添加消息函数声明:afx_msg int OnThreadSendmess();(3)在ThreadTestView.cpp文件中添加消息映射:ON_MESSAGE(WM_THREAD_SENDMESS,OnThreadSendmess)(4)OnThreadSendmess()代码如下:int CThreadTestView:OnThreadSendmess()m_iTime+;Invalidate();re
10、turn 0;第15页,共41页。2.使用消息实现线程间通信(5)修改ThreadProc()函数如下:UINT TreadProc(LPVOID param)CThreadTestApp*pApp=(CThreadTestApp*)AfxGetApp();CMainFrame*pMainFrame=(CMainFrame*)pApp-GetMainWnd();CThreadTestView*pView=(CThreadTestView*)pMainFrame-GetActiveView();pView-m_strMessage=启动了一个线程!;while(threadController)
11、:Sleep(1000);:PostMessage(HWND)param,WM_THREAD_SENDMESS,0,0);pView-m_iTime=0;pView-m_strMessage=线程结束!;return 0;第16页,共41页。基本知识1.PostMessage(HWND)param,WM_THREAD_SENDMESS,0,0);第17页,共41页。3.使用CEvent类实现线程间通信(1)Event对象:有两种状态:通信状态和非通信状态(2)创建一个CEvent类的对象很:CEvent threadStart;他自动的处在未通信状态(3)threadStart.SetEven
12、t();使其处于通信状态(4)调用WaitForSingleObject()来监视CEvent对象的::WaitForSingleObject(threadStart.m_hObject,INFINITE);第18页,共41页。3.使用CEvent类实现线程间通信 在上一节程序的基础上,进行以下操作:(1)在ThreadTestView.cpp中:#include afxmt.h(2)在ThreadTestView.cpp中加上下列两个全局变量:CEvent threadStart;CEvent threadEnd;(3)修改ThreadProc()函数(4)修改OnThreadStart()
13、函数中的内容。(5)修改OnThreadStop()函数中的内容。(6)CThreadTestView添加WM_CREATE消息处理函数OnCreate()。void CThreadTestView:OnThreadStart()threadStart.SetEvent();void CThreadTestView:OnThreadStop()threadEnd.SetEvent();第19页,共41页。3.使用CEvent类实现线程间通信 UINT TreadProc(LPVOID param)CThreadTestApp*pApp=(CThreadTestApp*)AfxGetApp();
14、CMainFrame*pMainFrame=(CMainFrame*)pApp-GetMainWnd();CThreadTestView*pView=(CThreadTestView*)pMainFrame-GetActiveView();:WaitForSingleObject(threadStart.m_hObject,INFINITE);pView-m_strMessage=启动了一个线程!;BOOL keepRunning=TRUE;while(keepRunning):Sleep(1000);int result=:WaitForSingleObject(threadEnd.m_hO
15、bject,0);if(result=WAIT_OBJECT_0)keepRunning=FALSE;:PostMessage(HWND)param,WM_THREAD_SENDMESS,0,0);pView-m_iTime=0;pView-m_strMessage=线程结束!;return 0;第20页,共41页。3.使用CEvent类实现线程间通信 int CThreadTestView:OnCreate(LPCREATESTRUCT lpCreateStruct)if(CView:OnCreate(lpCreateStruct)=-1)return-1;/TODO:Add your sp
16、ecialized creation code hereHWND hWnd=GetSafeHwnd();AfxBeginThread(TreadProc,hWnd,THREAD_PRIORITY_NORMAL);return 0;第21页,共41页。9.3 线程同步 学习目标1.使用Critical Section实现线程同步 2.使用互斥对象(mutex)实现线程同步 3.使用信号量(semaphore实现线程同步 返回第9章第22页,共41页。9.3.1 使用Critical Section 1.CCriticalSection criticalSection;2.criticalSect
17、ion.Lock();/访问前3.criticalSection.Unlock();/访问后第23页,共41页。9.3.1 使用Critical Section 一个使用Critical Section 的线程安全类。#include afxmt.hclass CIntArrayprivate:int array10;CCriticalSection criticalSection;public:CIntArray();CIntArray();void SetArray(int value);void GetArray(int dstArray10);第24页,共41页。9.3.1 使用Cri
18、tical Section 一个使用Critical Section 的线程安全类。#include stdafx.h#include IntArray.hCIntArray:CIntArray()CIntArray:CIntArray()void CIntArray:SetArray(int value)criticalSection.Lock();for(int x=0;x10;+x)arrayx=value;criticalSection.Unlock();void CIntArray:GetArray(int dstArray10)criticalSection.Lock();for(
19、int x=0;xGetMainWnd();CThreadTestView*pView=(CThreadTestView*)pMainFrame-GetActiveView();for(int i=1;iInvalidate();:Sleep(2000);return 0;第28页,共41页。9.3.1 使用Critical Section UINT ReadThreadProc(LPVOID param)CThreadTestApp*pApp=(CThreadTestApp*)AfxGetApp();CMainFrame*pMainFrame=(CMainFrame*)pApp-GetMai
20、nWnd();CThreadTestView*pView=(CThreadTestView*)pMainFrame-GetActiveView();while(1)intArray.GetArray(iArray1);pView-Invalidate();return 0;第29页,共41页。9.3.1 使用Critical Section void CThreadView:OnStartthread()/TODO:Add your command handler code hereHWND hWnd=GetSafeHwnd();AfxBeginThread(WriteThreadProc,h
21、Wnd);AfxBeginThread(ReadThreadProc,hWnd);第30页,共41页。9.3.1 使用Critical Section void CThreadTestView:OnDraw(CDC*pDC)char str70;strcpy(str,线程 1 为数组设置的值是:);int len=strlen(str);wsprintf(&strlen,%d,intSetNumber);pDC-TextOut(30,30,str);strcpy(str,线程 2 读到的值是:);for(int i=0;iTextOut(30,50,str);第31页,共41页。9.3.2 使
22、用使用Mutex(互斥对象)将上例中的critical section替换成Mutex#include afxmt.hclass CIntArrayprivate:int array10;CMutex mutex;public:CIntArray();CIntArray();void SetArray(int value);void GetArray(int dstArray10);第32页,共41页。9.3.2 使用使用Mutex(互斥对象)#include stdafx.h#include CIntArray.hCIntArray:CIntArray()CIntArray:CIntArra
23、y()void CIntArray:SetArray(int value)CSingleLock singleLock(&mutex);singleLock.Lock();for(int x=0;x10;+x)arrayx=value;void CIntArray:GetArray(int dstArray10)CSingleLock singleLock(&mutex);singleLock.Lock();for(int x=0;x10;+x)dstArrayx=arrayx;第33页,共41页。9.3.3 使用信号量(Semaphore)(1)创建信号量CSemaphore Semapho
24、re(2,2);(2)创建一个CSingleLock对象 CSingleLock singleLock(semaphore);(3)减小信号量计数器 singleLock.Lock();(4)增加信号量计数器 singleLock.Unlock();第34页,共41页。使用信号量的一个例子 1.修改CIntArray类#include afxmt.hclass CIntArrayprivate:int array10;CMutex mutex;CSemaphore*semaphore;public:CIntArray();CIntArray();void SetArray(int value)
25、;void GetArray(int dstArray10);第35页,共41页。使用信号量的一个例子 1.修改CIntArray类 CIntArray:CIntArray()semaphore=new CSemaphore(2,2);CIntArray:CIntArray()delete semaphore;void CIntArray:SetArray(int value)CSingleLock singleLock(&mutex);singleLock.Lock();for(int x=0;x10;+x)arrayx=value;void CIntArray:GetArray(int d
26、stArray10)CSingleLock singleLock(semaphore);singleLock.Lock();for(int x=0;xGetMainWnd();CThreadTestView*pView=(CThreadTestView*)pMainFrame-GetActiveView();while(1)intArray.GetArray(iArray2);pView-Invalidate();return 0;第39页,共41页。使用信号量的一个例子 UINT ReadThreadProc3(LPVOID param)CThreadTestApp*pApp=(CThrea
27、dTestApp*)AfxGetApp();CMainFrame*pMainFrame=(CMainFrame*)pApp-GetMainWnd();CThreadTestView*pView=(CThreadTestView*)pMainFrame-GetActiveView();while(1)intArray.GetArray(iArray3);pView-Invalidate();return 0;.5 5改写视类的改写视类的OnDrawOnDraw()函数()函数 第40页,共41页。使用信号量的一个例子 void CThreadTestView:OnDraw(CDC*pDC)CTh
28、readTestDoc*pDoc=GetDocument();ASSERT_VALID(pDoc);/TODO:add draw code for native data herechar str70;strcpy(str,写线程为数组设置的值是:);int len=strlen(str);wsprintf(&strlen,%d,intSetNumber);pDC-TextOut(30,30,str);strcpy(str,读线程 1 读到的值是:);for(int i=0;iTextOut(30,50,str);strcpy(str,读线程 2 读到的值是:);for(i=0;iTextOut(30,70,str);strcpy(str,读线程 3 读到的值是:);for(i=0;iTextOut(30,90,str);strcpy(str,读线程 4 读到的值是:);for(i=0;iTextOut(30,110,str);strcpy(str,读线程 5 读到的值是:);for(i=0;iTextOut(30,130,str);strcpy(str,读线程 6 读到的值是:);for(i=0;iTextOut(30,150,str);strcpy(str,读线程 7 读到的值是:);for(i=0;iTextOut(30,170,str);返回第9章第41页,共41页。