【教学课件】第11章多线程编程.ppt

上传人:小飞机 文档编号:5657730 上传时间:2023-08-06 格式:PPT 页数:22 大小:483.50KB
返回 下载 相关 举报
【教学课件】第11章多线程编程.ppt_第1页
第1页 / 共22页
【教学课件】第11章多线程编程.ppt_第2页
第2页 / 共22页
【教学课件】第11章多线程编程.ppt_第3页
第3页 / 共22页
【教学课件】第11章多线程编程.ppt_第4页
第4页 / 共22页
【教学课件】第11章多线程编程.ppt_第5页
第5页 / 共22页
点击查看更多>>
资源描述

《【教学课件】第11章多线程编程.ppt》由会员分享,可在线阅读,更多相关《【教学课件】第11章多线程编程.ppt(22页珍藏版)》请在三一办公上搜索。

1、第11章 多线程编程,在Windows平台上可以同时执行多个任务。例如,可以在使用Word的同时使用VC;使用Word的时候又可以一边打印文档,一边继续编辑文档。Word和VC的并行运行叫进程,在Word中一边打印一边编辑叫做线程。也就是说,操作系统内可以同时运行多个进程,而进程内又可以有多个线程。可以看到,多线程可以提高程序的并发性,同时也可以提高程序的工作效率。例如,M个人搬N个箱子,那么当然是M的值越大搬的速度越快。线程也是如此,例如经常听说某某软件采用多线程下载,速度非常快,这就是多线程的功劳。但是这里要注意,在单CPU平台下,多线程程序在某一时刻只有一个线程在运行,多线程程序运行是交

2、替进行的。如果是多个CPU,当然就有可能多个线程同时运行。虽然单CPU平台每一时刻只能有一个线程在工作,但是这种机制可以让作业快的线程有机会先执行完毕,并且在有时多线程的很多操作与操作系统有关,使用多线程确实能在很大程度上提高程序工作的效率,提高程序的并发性。至于线程间如何切换交替运行,到后面第11.3节线程同步中会结合例子说明。,11.1 如何创建线程,创建一个线程主要分为两步,即首先调用API函数CreateThread创建一个线程,然后编写线程函数。创建线程完毕以后,主线程会与创建后的线程同时运行。,11.1.1 使用CreateThread创建一个线程,实际上,main函数或者WinM

3、ain函数在运行以后就是一个线程通常叫主线程,所以可以将线程简单地理解为一个函数。API函数CreateThread可以创建一个线程来指向当前进程的虚拟地址空间,也就是说执行一个函数。下面是这个函数的原型。HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes,SIZE_T dwStackSize,LPTHREAD_START_ROUTINE lpStartAddress,LPVOID lpParameter,DWORD dwCreationFlags,LPDWORD lpThreadId);,11.1.2 编写线程函数,Cre

4、ateThread函数的第3个参数需要设置一个线程函数地址。CreateThread创建线程成功以后,新的线程就从这个函数执行。线程函数必须定义成下面的形式。DWORD WINAPI ThreadProc(LPVOID lpParameter);CreateThread的第4个参数lpParameter将通过lpParameter被传递进来。,11.1.3 示例创建一个线程,下面代码显示如何用API函数创建一个线程。在这个线程中输出“Hello MyThread”。程序在开始时候创建了一个线程,并且传递了2个主要的参数进去,其他的参数都使用建议值。第3个参数表示线程函数,第4个参数表示给线程函

5、数的参数。线程函数名叫MyThreadProc,参数值是“Hello MyThreadn”,这样MyThreadProc就会在lpParameter中得到这个“Hello MyThreadn”。由于参数类型是LPVOID(void*),所以需要将LPVOID再转换成char*或者const char*类型,到此接收参数工作完毕,开始用printf输出。,11.1.4 示例创建多个线程,模拟机票订购,在上一小节中讲述了如何创建线程,下面用一个经典的例子来阐述如何创建多个线程。一架飞机有200个座位,现有两个订购地点提供订票服务。飞机票的编号从1200,现在用多线程实现订购过程。代码如下。可以看到

6、,利用第1节学过的知识,上述程序中创建了2个线程,分别模拟2个飞机售票处,飞机票由两个售票处交替地售出。但是这种售票方式存在一个问题,下节将会介绍。,11.2 线程同步,在多线程程序执行过程中,会有多个线程访问同一块数据临界资源,那么就需要对临界资源进行读写保护。完成这一工作的过程就是线程同步。上面一节的最后一个例子中,创建2个线程模拟2个飞机售票处售票的过程,200张飞机票由2个线程交替出售,这其中存在一个问题,假如修改一下Shop1的while循环代码,如下所示。while(1)if(tickets 0)Sleep(1);/这里添加一行,用来延时printf(Shop1 Sell a Ti

7、cket:%dn,tickets);tickets-;elsereturn 0;,11.2.1 线程之间切换时间片,本章的开始曾经介绍过,单CPU平台下某一时刻只能有一个线程在运行。但是多线程程序的运行与并行运行相似。这是因为操作系统将CPU的运行时间分成多个小块,每个小块被称为一个时间片。每个线程都运行一个时间片,当时间片限定的时间到,还在运行的线程就将CPU交给其他线程使用,如图所示,假设时间片大小为0.01ms,则线程1运行了0.01ms后暂停,将CPU交给线程2使用。当线程2运行0.01ms后,再将CPU交给线程3使用,依此类推,直到所有线程都运行结束。由于时间片是如此的小,以至于人们

8、无法感觉到,所以多个线程看似在“并行”地运行。,11.2.2 使用互斥对象来同步线程,前面说过,在多线程程序设计过程中,对于临界资源的读写需要进行保护。也就是说,一个线程正在对这个临界资源进行读写时,其他线程就不能对其进行读写,这个过程叫做线程的同步。线程同步是通过一些操作系统的内核对象来实现的。通常,内核对象有事件对象、互斥对象等,其功能就是为了对临界资源进行保护。这里先介绍一下如何使用互斥对象。API函数CreateMutex可以用来创建一个互斥对象,其原型如下。HANDLE CreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes,BOOL

9、bInitialOwner,LPCTSTR lpName);,11.2.3 示例修改多线程例子,使用互斥对象对其进行同步,下面在飞机售票系统的原代码中添加一些代码,使一个线程在操作临界资源时,其他线程不能操作。具体代码如下。/eg_2_multithread_selltickets2.cpp飞机售票系统-修改以后#include stdafx.h#include#include DWORD WINAPI Shop1(LPVOID lpParameter);,11.2.4 示例使用命名互斥对象判断程序是否多次运行,前面一节讲述了如何使用互斥对象来进行线程同步,使用API函数CreateMutex

10、可以创建一个互斥对象,但是第3个参数使用的是NULL,表示不起名字。本节通过一个例子,告诉读者互斥对象的另外一个用途命名互斥对象判断程序是否运行多次。编写一个程序,正常运行输出“Hello world”,如果重复运行输出“Sorry”。,11.3 线程同步的其他方法,除了互斥对象,还有其他方法可以实现线程的同步。不同的方法适用于不同的场合,但是读者只要掌握其中一种就可满足大部分多线程应用,下面对其他方法做一简单介绍。,11.3.1 示例使用事件对象完成线程的同步,下面给出使用事件对象完成线程同步的飞机售票系统。粗体字表示与互斥对象示例不同的地方。,11.3.2 示例使用临界区完成线程的同步,事

11、件对象和互斥对象的使用非常相像。下面的例子使用临界区完成线程同步,其代码也与上面2个示例的代码非常相像,粗体字表示与上面2个示例代码不同的地方。,11.4 线程同步导致的死锁及预防,由于线程在同步的时候会调用一些方法来等待,当发生一些事件以后再继续运行,但是如果一个线程发生了永远等待的现象,那么这个现象就称为死锁。例如在线程函数中,一些分支语句中经常会有return关键字,如果这个分支语句在EnterCriticalSection和LeaveCriticalSection之间,那么这个代码就是危险的。其中存在的问题以示例演示。,11.4.1 示例使用临界区时,意外退出导致死锁,上一节的最后一个

12、示例存在一个问题,就是有一个线程永远不会退出。下面添加一些输出项来。测试其是否没有退出。输出结果表明,Shop1没有调用return返回,也就是说,Shop1一直等待,这种一直等待的现象就是死锁。造成这种现象的原因很明显,Shop2调用EnterCriticalSection进入临界区,然后操作临界资源,但是由于tickets等于0,导致其调用return直接返回,没有运行到LeaveCriticalSection离开临界区。当然,这就使正在使用EnterCriticalSection等待进入临界区的Shop1就永远也进不去。,11.4.2 预防死锁,死锁在实际开发中也是非常容易出现的,其原因

13、可能是程序员不良的编程规范、代码的重复开发以及一些其他因素造成的。所以为了预防死锁,程序员应该养成良好的编程规范,并且在设计上让程序合理。例如,上面的情况使用互斥对象就不会出现死锁,因为互斥对象的拥有者线程会在结束时,放弃对其使用权,其他线程可以接着拥有这个互斥对象。而临界区不可以。,11.5 应用案例使用多线程完善文件分割工具,在前面章节的应用案例中,已经制作了一个文件分割工具。但是程序存在一个问题,就是当单击“开始分割”或“开始合并”的时候,程序会无法响应,直到操作完成才能继续操作。本节将通过线程来解决以上问题。,11.5.1 案例效果图,程序界面与第5章的程序界面基本一致。当选择“分割文

14、件”选项卡时,程序界面如图所示。当选择“合并文件”选项卡时,程序界面如图所示。,11.5.2 需要掌握的知识点,本案例中需要掌握的知识点如下。对话框编程基础;文件操作基础;多线程编程;基本控件的使用。,11.5.3 开发过程,由于本案例属于先前案例的升级版,所以可以在先前案例中稍作修改,同时也避免了界面设计的麻烦。具体操作步骤如下。(1)创建第5章应用案例工程复本,将项目目录重命名为eg_6_filesplitter_plus。(2)打开工程,首先修改CFileSplitter类的声明,下列代码中粗体字表示修改部分。(3)在构造函数中初始化新添加的成员pTargetWnd。(4)修改Split

15、与Join,只在其中创建线程,分割与合并的工作交给线程处理。(5)编写线程函数,完成分割与合并的功能。(6)至此,文件分割类CFileSplitter已经修改完毕。(7)当分割文件结束,CFileSplitter对象会向分割窗口发送UM_SPLITOK消息,手动添加UM_SPLITOK消息处理。(8)当合并文件结束,CFileSplitter对象会向分割窗口发送UM_JOINOK消息,手动添加UM_JOINOK消息处理。,11.6 小结,本章主要学习了多线程编程的相关知识,了解了多线程的优点及多线程同步、死锁等相关概念,最后通过一个应用案例,使用多线程解决前面案例中的不足。使用多线程提高了程序的并发,同时也带来管理上的复杂度,所以多线程编程不是任何时候都适用的。当多个线程对同一数据进行读写时,必须为此数据采取同步措施。如果将线程函数定义为类成员,必须将函数定义为static函数。,

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 生活休闲 > 在线阅读


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号