欢迎来到天天文库
浏览记录
ID:51493201
大小:308.34 KB
页数:37页
时间:2020-03-25
《多线程编程基础.pdf》由会员上传分享,免费在线阅读,更多相关内容在行业资料-天天文库。
Windows编程基础1 第九章多线程编程基础本课程旨在向学员介绍:1)线程基础知识时间:学时32)进程与线程的区别教学方法:讲授+课上3)使用WIN32和MFC编写多线练习+课下作业程程序2 多线程概述•大多数操作系统,可以同时运行几个程序,操作系统的这种能力称之为多任务处理。•Windows操作系统用多进程/线程机制提供对一个应用程序内多任务的支持,进程与线程之间是密不可分的,线程依附于进程,一个进程可包含多个线程。3 多线程的引入-1/2•按照如下步骤创建应用程序:–创建一个基于对话框的应用程序,名字是SingleThread–在对话框中添加一个按钮。–添加响应函数代码:–Sleep(6000);•观察程序的运行效果.–点击按钮后,程序在这六秒钟像“死机”一样。–原因分析:因为Sleep函数一直独占CPU的时间。点击“Sleep”CPU一直运行Sleep(6秒)6秒结束执行其他内容4 多线程的引入-2/2•引入多线程的原因是单线程容易阻塞!•如何解决?•首先,要有抢先式多任务操作系统为基础•最好能够生成两个控制流程:–一个负责其中的长时间处理过程。–另外一个负责响应用户及系统消息,各司其职,则上述对话框的界面就会得到及时更新。5 抢先式多任务操作系统•工作原理:在WIN32系统中,程序对CPU的占用时间是由系统分配的,而不是由程序决定的.•当系统分配的时间片到时收回CPU的控制权交给其它程序,这种管理方式称抢先式多任务.•优点:–一个应用程序崩溃一般不会造成死机.–在进行后台费时的工作时不会导致系统挂起.6 进程和线程的概述-1/3•系统既可以同时启动多个进程,也支持同时运行多个线程,它们之间有什么差别?•进程和线程都是操作系统的概念。•进程是什么?–进程是应用程序的执行实例,每个进程是由私有的虚拟地址空间、代码、数据和其它各种系统资源组成,进程在运行过程中创建的资源随着进程的终止而被销毁,所使用的系统资源在进程终止时被释放或关闭。7 进程和线程的概述-2/3•线程是什么?–线程是进程内部的一个执行单元。系统创建好进程后,实际上就启动执行了该进程的主执行线程,主执行线程以函数地址形式,比如说WinMain函数,将程序的启动点提供给Windows系统。主执行线程终止了,进程也就随之终止。–每一个进程至少有一个主执行线程,它无需由用户去主动创建,是由系统自动创建的。用户根据需要在应用程序中创建其它线程,多个线程并发地运行于同一个进程中。一个进程中的所有线程都在该进程的虚拟地址空间中,共同使用这些虚拟地址空间、全局变量和系统资源,所以线程间的通讯非常方便,多线程技术的应用也较为广泛。8 进程和线程的概述-3/3•多线程编程在Win32方式下和MFC类库支持下的原理是一致的,进程的主线程在任何需要的时候都可以创建新的线程。当线程执行完后,自动终止线程;当进程结束后,所有的线程都终止。所有活动的线程共享进程的资源,因此,在编程时需要考虑在多个线程访问同一资源时产生冲突的问题。当一个线程正在访问某进程对象,而另一个线程要改变该对象,就可能会产生错误的结果,编程时要解决这个冲突.9 为什么不使用多进程-1/2•1)由于创建新进程必须加载代码,而线程要执行的代码已经被映射到进程的地址空间,所以创建、执行线程的速度比进程更快。•2)一个进程的所有线程共享进程的地址空间和全局变量,所以简化了线程之间的通讯10 为什么不使用多进程-2/2•使用多进程,最困难的是如何把窗口句柄交给另一个进程。在Win32中,句柄只在他的诞生进程中才有意义,这样避免某个进程危及另一个进程资源。而在一个多线程程序中,所有线程都可以使用这个窗口句柄,因为句柄和线程在同一进程中。•虽然请求产生新进程也容易,但他的额外负担非常惊人。必须载入服务器软件的一个全新副本,配置大量内存并初始化,新副本必须与原先状态相同,数秒钟可能就这样过去了…11 使用线程要注意•使用线程并非没有代价。可能引发潜在的严重为题,需要小心设计。•写线程:小心做计划:谁要什么?何时要?怎么要?12 编制多线程程序两种方式•Win32API–32位Windows环境下的Win32API提供了多线程应用程序开发所需要的接口函数。•MFC方式–VC中提供的标准C库也可以开发多线程应用程序,相应的MFC类库封装了多线程编程的类,用户在开发时可根据应用程序的需要和特点选择相应的工具。13 Win32API下的多线程编程•Win32API是Windows操作系统内核与应用程序之间的界面,它将内核提供的功能进行函数包装,应用程序通过调用相关函数而获得相应的系统功能。为了向应用程序提供多线程功能,Win32API函数集中提供了一些处理多线程程序的函数集。•应用Win32API进行程序设计的优点:基于Win32的应用程序执行代码小,运行效率高,但是它要求程序员编写的代码较多,且需要管理所有系统提供给程序的资源。•要求:用Win32API直接编写程序要求程序员对Windows系统内核有一定的了解,会占用程序员很多时间对系统资源进行管理,因而程序员的工作效率降低。14 WIN32API多线程相关函数-1/5•Win32提供了一系列的API函数来完成线程的创建、挂起、恢复、终结以及通信等工作。下面将选取其中的一些重要函数进行说明,首先介绍CreateThread函数1:HANDLECreateThread(LPSECURITY_ATTRIBUTESlpThreadAttributes,DWORDdwStackSize,LPTHREAD_START_ROUTINElpStartAddress,LPVOIDlpParameter,DWORDdwCreationFlags,LPDWORDlpThreadId);•该函数在其调用进程的进程空间里创建一个新的线程,并返回已建线程的句柄15 WIN32API多线程相关函数-2/5•参数说明:–lpThreadAttributes(参数1):是指向一个SECURITY_ATTRIBUTES结构的指针,该结构决定了线程的安全属性,一般置为NULL;–dwStackSize(参数2):指定了线程的堆栈深度,一般都设置为0;–lpStartAddress(参数3):表示新线程开始执行时代码所在函数的地址,即线程的起始地址。一般情况为(LPTHREAD_START_ROUTINE)ThreadFunc,ThreadFunc是线程函数名;16 WIN32API多线程相关函数-3/5•参数说明:–lpParameter(参数4):指定了线程执行时传送给线程的32位参数,即线程函数的参数;–dwCreationFlags(参数5):控制线程创建的附加标志,可以取两种值。如果该参数为0,线程在被创建后就会立即开始执行;如果该参数为CREATE_SUSPENDED,则系统产生线程后,该线程处于挂起状态,并不马上执行,直至函数ResumeThread被调用;–lpThreadId(参数6):该参数返回所创建线程的ID;17 WIN32API多线程相关函数-4/5•其他函数说明:–2:DWORDSuspendThread(HANDLEhThread);–该函数用于挂起指定的线程,如果函数执行成功,则线程的执行被终止。–3:DWORDResumeThread(HANDLEhThread);–该函数用于结束线程的挂起状态,执行线程。–4:VOIDExitThread(DWORDdwExitCode);–该函数用于线程终结自身的执行,主要在线程的执行函数中被调用。其中参数dwExitCode用来设置线程的退出码。18 WIN32API多线程相关函数-5/5–5:BOOLTerminateThread(HANDLEhThread,DWORDdwExitCode);•一般情况下,线程运行结束之后,线程函数正常返回,但是应用程序可以调用TerminateThread强行终止某一线程的执行–6:BOOLPostThreadMessage(DWORDidThread,UINTMsg,WPARAMwParam,LPARAMlParam);注意:与PostMessage比较19 创建WIN32多线程实例1-1/6•创建一个实例,实现如下功能–在对话框中有两个按钮,一个启动,一个停止。–对话框界面包含一个编辑框,用于显示当前系统时间。–编辑框属性为只读,初始时只有启动按钮好用。–按下“启动”后,“启动”按钮变为无效,停止按钮变为有效。•解决的方法有两种:•方法一:通过定时器的方法•方法二:使用多线程的方法20 创建WIN32多线程实例1-2/6•创建过程:–1:创建一个基于对话框的应用程序(WIN32MultiThread),在对话框中添加两个按钮,”启动”和”停止”按钮–2:在对话框头文件中对话框类外添加线程函数声明voidThreadFunc();–3:在对话框头文件中添加保护类成员变量HANDLEm_hThread;DWORDm_dwThreadID;–4:在对话框类源文件中添加全局变量g_bRun21 创建WIN32多线程实例1-3/6–5:实现线程函数voidThreadFunc(){CTimetime;CStringstrTime;while(1){while(g_bRun){time=CTime::GetCurrentTime();strTime=time.Format("%H:%M:%S");::SetDlgItemText(AfxGetMainWnd()->m_hWnd,IDC_EDIT_TIME,strTime);Sleep(1000);}}}22 创建WIN32多线程实例1-4/6–6:OnInitDialog成员函数的实现voidCWIN32MultiThreadDlg::OnInitDialog(){…g_bRun=FALSE;m_hThread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadFunc,NULL,0,&ThreadID);GetDlgItem(IDC_START)->EnableWindow(FALSE);GetDlgItem(IDC_STOP)->EnableWindow(TRUE);}23 创建WIN32多线程实例1-5/6–7:OnStart成员函数的实现voidCWIN32MultiThreadDlg::OnButtonStart(){GetDlgItem(IDC_BUTTON_START)->EnableWindow(FALSE);GetDlgItem(IDC_BUTTON_STOP)->EnableWindow(TRUE);g_bRun=TRUE;if(m_hThread)ResumeThread(m_hThread);}24 创建WIN32多线程实例1-6/6–7:OnStop成员函数的实现voidCWIN32MultiThreadDlg::OnButtonStop(){GetDlgItem(IDC_BUTTON_START)->EnableWindow(TRUE);GetDlgItem(IDC_BUTTON_STOP)->EnableWindow(FALSE);g_bRun=FALSE;}25 创建WIN32多线程实例2-1/3•实现向线程函数传送数据•建一个实例,实现如下功能–在对话框中有一个启动按钮和一个编辑框,编辑框用于接收用户的输入。–程序根据编辑框的输入决定输出响声的次数。•实现步骤与上一题目类似26 创建WIN32多线程实例2-2/3•关键函数1voidThreadFunc(intinteger){inti;CStringstr;str.Format("%d",integer);AfxMessageBox(str);for(i=0;iEnableWindow(TRUE);}28 MFC创建线程函数-1/3•在MFC中,一般用全局函数AfxBeginThread()来创建并初始化一个线程的运行。CWinThread*AfxBeginThread(AFX_THREADPROCpfnThreadProc,LPVOIDpParam,intnPriority=THREAD_PRIORITY_NORMAL,UINTnStackSize=0,DWORDdwCreateFlags=0,LPSECURITY_ATTRIBUTESlpSecurityAttrs=NULL);29 MFC创建线程函数-2/3•参数pfnThreadProc(参数1)是线程执行体函数,函数原形为:UINTThreadFunction(LPVOIDpParam)。•参数pParam(参数2)是传递给执行函数的参数;•参数nPriority(参数3)是线程执行权限,可选值:–THREAD_PRIORITY_NORMAL、THREAD_PRIORITY_LOWEST、THREAD_PRIORITY_HIGHEST、THREAD_PRIORITY_IDLE。•参数dwCreateFlags(参数4)是线程创建时的标志,可取值CREATE_SUSPENDED,表示线程创建后处于挂起状态,调用ResumeThread函数后线程继续运行,或者取值“0”表示线程创建后处于运行状态。30 MFC创建线程函数-3/3•关于返回值的说明:–返回值是CWinThread类对象指针,它的成员变量m_hThread为线程句柄,在Win32API方式下对线程操作的函数参数都要求提供线程的句柄,所以当线程创建后可以使用所有Win32API函数对pWinThread->m_Thread线程进行相关操作。–注意:如果在一个类对象中创建和启动线程时,应将线程函数定义成类外的全局函数。31 创建MFC多线程实例-1/6•创建一个程序:包含一个菜单,用于启动一个线程和启动一个对话框,在窗口显示运行时间及相关信息32 创建MFC多线程实例-2/6运行机制(2)调用(1)点击“启动线程”CThreadTestView::OnStart()(3)调用ThreadProc(LPVOIDparam)(5)调用(4)点击“其他线程”CThreadTestView::OnDoOther()33 创建MFC多线程实例-3/6•利用AppWizard生成单文档应用程序框架(ThreadTest)•编辑菜单资源(启动线程,其它任务),添加对话框,用于菜单启动•添加两个变量CStringm_strMessage;//存储文字intm_iTime;//存储显示数字•构造函数初始化变量CThreadTestView::CThreadTestView(){m_strMessage="没有启动线程";m_iTime=0;}•头文件包含#include"MainFrm.h"34 创建MFC多线程实例-4/6•视图输出函数voidCThreadTestView::OnDraw(CDC*pDC){CThreadTestDoc*pDoc=GetDocument();ASSERT_VALID(pDoc);charchNumber[6];itoa(m_iTime,chNumber,10);pDC->TextOut(30,30,m_strMessage);pDC->TextOut(30,50,chNumber);}35 创建MFC多线程实例-5/6这个函数创建并初始化点击“启动线程”时,系统会调用一个线程,参数pfnThreadProc是CThreadTestView::OnStart()函数线程执行体函数,正是通过它来调用ThreadProc(LPVOIDparam)。每点击“启动先线程”一下,都会创建voidCThreadTestView::OnStart()一个新线程。{HWNDhWnd=GetSafeHwnd();AfxBeginThread(ThreadProc,hWnd,THREAD_PRIORITY_NORMAL);}点击“其它线程”时,系统会调用CThreadTestView::OnThreadOther()函数voidCThreadTestView::OnThreadOther(){CDlgThreaddlg;dlg.DoModal();}36 创建MFC多线程实例-6/6UINTThreadProc(LPVOIDparam){用到MainFrm中的CMainFrame类,CThreadTestApp*pApp=所以ThreadTestView.cpp需要(CThreadTestApp*)AfxGetApp();包含头文件MainFrm.hCMainFrame*pMainFrame=(CMainFrame*)pApp->GetMainWnd();CThreadTestView*pView=改变M_strMessage的值(CThreadTestView*)pMainFrame->GetActiveView();pView->m_strMessage="启动了一个线程!";while(pView->m_iTime<50){::Sleep(1000);pView->m_iTime++;pView->Invalidate();while循环中,每隔一秒}m_iTime的值会加1。pView->m_iTime=0;循环结束后,m_iTimepView->m_strMessage="线程结束";的值又会设为0,M_strMessagereturn0;会设为“线程结束”}37
此文档下载收益归作者所有
举报原因
联系方式
详细说明
内容无法转码请点击此处