




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、 线程(线程(Thread)是程序中一个控制的)是程序中一个控制的执行流程,属进程的一个实体。执行流程,属进程的一个实体。 多任务操作系统就是采用多进程和多多任务操作系统就是采用多进程和多线程机制,即在同一时间内有多个进程在线程机制,即在同一时间内有多个进程在运行,一个进程可以有若干个线程即为多运行,一个进程可以有若干个线程即为多线程。线程。 采用多线程技术的目的是提高程序运采用多线程技术的目的是提高程序运行速度和行速度和CPU资源利用率。资源利用率。 本章主要讨论本章主要讨论MFC的多线程程序设计的多线程程序设计方法。方法。多线程基础多线程基础14.1多线程编程多线程编程14.2线程的终止线
2、程的终止14.3线程的优先级与管理线程的优先级与管理14.4线程之间的通信线程之间的通信14.5线程的同步线程的同步14.614.1 多线程基础多线程基础 多线程技术是多任务并发程序设计的多线程技术是多任务并发程序设计的基础,是提高程序运行速度和增强数据处基础,是提高程序运行速度和增强数据处理能力的重要技术手段。理能力的重要技术手段。 实际中,常常采用多线程编程技术提实际中,常常采用多线程编程技术提高应用程序的性能。高应用程序的性能。14.1.1 14.1.1 进程与线程进程与线程 进程(进程(Process)是程序在计算机内存)是程序在计算机内存中的运行活动,是系统资源分配和调度的中的运行活
3、动,是系统资源分配和调度的基本单位,是程序(基本单位,是程序(Program)即指令集)即指令集合和相关数据的动态体现。合和相关数据的动态体现。 进程和程序的区别如下:进程和程序的区别如下: (1)进程描述的是程序的动态行为,)进程描述的是程序的动态行为,而程序是一个指令序列和相关数据的静态而程序是一个指令序列和相关数据的静态描述;描述; (2)进程是程序的一次运行活动,具)进程是程序的一次运行活动,具有暂时性,而程序可以脱离机器长期保存,有暂时性,而程序可以脱离机器长期保存,具有永久性;具有永久性; (3)一个程序可以对应多个进程,一)一个程序可以对应多个进程,一个进程也可以有多个程序,二者
4、没有确定个进程也可以有多个程序,二者没有确定的对应关系;的对应关系; 进程是为了刻画程序内部运行状态而进程是为了刻画程序内部运行状态而引入的一个概念。引入的一个概念。 线程是由进程创建的可执行单元,线线程是由进程创建的可执行单元,线程依附于进程的存在而共享进程的内存空程依附于进程的存在而共享进程的内存空间,由应用程序提供多个线程执行控制。间,由应用程序提供多个线程执行控制。 线程也可以创建线程。线程和进程一线程也可以创建线程。线程和进程一样具有一个生存周期,在这个生存周期中,样具有一个生存周期,在这个生存周期中,总是处于某种状态之中,诸如就绪态、运总是处于某种状态之中,诸如就绪态、运行态或等待
5、(阻塞)态。行态或等待(阻塞)态。 当进程退出运行以后,线程也随之消当进程退出运行以后,线程也随之消失,所占用的资源也一同释放。失,所占用的资源也一同释放。 所以,在下列情况可以采用多线程编所以,在下列情况可以采用多线程编程技术:程技术: (1)为了提高运行效率,在同一时间内)为了提高运行效率,在同一时间内运行多个任务时;运行多个任务时; (2)处理数据量比较大,需要等待时;)处理数据量比较大,需要等待时; (3)同一程序内没有顺序关系的代码段)同一程序内没有顺序关系的代码段时;时; (4)应用系统采用客户)应用系统采用客户/服务器机制时。服务器机制时。 程序设计使用多线程还要注意以下几程序设
6、计使用多线程还要注意以下几点:点: (1)多线程实际是多个程序段同时存)多线程实际是多个程序段同时存在并运行于内存中,不要让太多的线程使在并运行于内存中,不要让太多的线程使系统逻辑结构变得复杂,线程并不是越多系统逻辑结构变得复杂,线程并不是越多越好;越好; (2)有些情况下需要处理线程的同步)有些情况下需要处理线程的同步问题;问题; (3)搞清楚线程的优先级设置和调度)搞清楚线程的优先级设置和调度规则;规则; (4)搞清楚线程间的数据交换和通讯)搞清楚线程间的数据交换和通讯问题。问题。 14.1.2 14.1.2 线程分类线程分类 MFC将将Windows的线程处理功能封装的线程处理功能封装成
7、一个成一个CWinThread线程类,使程序设计更线程类,使程序设计更加方便简捷。加方便简捷。 MFC将将Windows线程分为两类:一类线程分为两类:一类是用于人机交互、处理用户输入的线程,是用于人机交互、处理用户输入的线程,称为用户界面线程(称为用户界面线程(User-Interface Threads);另一类是完成不需要用户干);另一类是完成不需要用户干预的或后台执行的操作,称为工作者线程预的或后台执行的操作,称为工作者线程或辅助线程(或辅助线程(Worker Threads)。)。 但对于但对于Windows API来说,由于它没来说,由于它没有引入类的概念,所以没有用户界面线程有引
8、入类的概念,所以没有用户界面线程和工作者线程之分,将二者等同处理,统和工作者线程之分,将二者等同处理,统称为线程。称为线程。14.2 多线程编程多线程编程 程序设计中采用多线程编程技术,可程序设计中采用多线程编程技术,可以有两种途径实现。以有两种途径实现。 一是采用一是采用Windows API提供的进程创提供的进程创建函数建函数CreateThread()进行创建,用挂起、进行创建,用挂起、恢复和终止、优先级控制等函数实现线程恢复和终止、优先级控制等函数实现线程管理;二是采用管理;二是采用MFC提供的提供的AfxBeginThread()函数创建,由函数创建,由CWinThread类进行管理
9、。类进行管理。 CWinThread封装了封装了Win32 API有关有关线程操作的函数。线程操作的函数。 本节在讲述本节在讲述Win32 API线程处理函数线程处理函数的基础上,主要讨论用的基础上,主要讨论用MFC提供的线程函提供的线程函数创建工作者线程和用户界面线程的方法。数创建工作者线程和用户界面线程的方法。 用户界面线程和工作者线程的主要区用户界面线程和工作者线程的主要区别是:前者需要响应用户输入和处理系统别是:前者需要响应用户输入和处理系统产生的事件与消息,拥有自己的消息队列产生的事件与消息,拥有自己的消息队列和消息循环;而后者不需要处理这些消息和消息循环;而后者不需要处理这些消息和
10、事件,没有消息队列和消息循环。和事件,没有消息队列和消息循环。14.2.1 Win32 API14.2.1 Win32 API线程处理线程处理 Windows API提供的线程操作函数有提供的线程操作函数有创建、挂起、恢复、终止以及优先级控制创建、挂起、恢复、终止以及优先级控制等。下面简单介绍这几个函数。等。下面简单介绍这几个函数。1 1线程创建线程创建 函数原型如下:函数原型如下: HANDLE CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes, DWORD dwStackSize, LPTHREAD_START_ROUTINE l
11、pStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId );2 2线程挂起线程挂起 函数原型如下:函数原型如下: DWORD SuspendThread( HANDLE hThread)3 3线程恢复线程恢复 函数原型如下:函数原型如下: DWORD ResumeThread(HANDLE hThread);14.2.2 14.2.2 工作者线程工作者线程 工作者线程是由工作者线程是由MFC提供的函数创建提供的函数创建的,它用于后台处理一些费时操作,如大的,它用于后台处理一些费时操作,如大数据量计
12、算、文件读写等操作,它没有消数据量计算、文件读写等操作,它没有消息循环。工作者线程的创建比较简单,直息循环。工作者线程的创建比较简单,直接调用接调用MFC的的AfxBeginThread()函数就可函数就可以。以。 该函数原型如下:该函数原型如下: CWinThread* AfxBeginThread( AFX_THREADPROC pfnThreadProc, LPVOID pParam, int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRI
13、BUTES lpSecurityAttrs = NULL ); 【例例14-1】创建一个工作者线程。创建一个工作者线程。步骤如下:步骤如下: 图图14.1 工作者线程设计阶段外观工作者线程设计阶段外观 图图14.2 工作者线程运行时界面工作者线程运行时界面 14.2.3 14.2.3 用户界面线程用户界面线程 用户界面线程就是响应界面操作的线用户界面线程就是响应界面操作的线程,就像程,就像MFC中创建的对话框一样。中创建的对话框一样。 当应用程序需要处理用户输入并响应当应用程序需要处理用户输入并响应系统产生的事件和消息时,可以通过创建系统产生的事件和消息时,可以通过创建用户界面线程实现。用户界
14、面线程实现。 在在MFC中创建用户界面线程比创建工中创建用户界面线程比创建工作者线程稍麻烦些,仍然需要调用作者线程稍麻烦些,仍然需要调用MFC的的AfxBeginThread()函数的另一个版本。函数的另一个版本。 使用这个函数创建用户界面线程时,使用这个函数创建用户界面线程时,函数的入口参数与工作者线程有所区别。函数的入口参数与工作者线程有所区别。 该函数原型如下:该函数原型如下: CWinThread* AfxBeginThread( CRuntimeClass* pThreadClass, int nPriority = THREAD_PRIORITY_NORMAL, UINT nSta
15、ckSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL ); 【例例14-2】创建一个自己的用户界创建一个自己的用户界面线程,具体步骤如下:面线程,具体步骤如下: 图图14.3 用户界面线程主窗口设计阶段外观用户界面线程主窗口设计阶段外观 图图14.4 用户界面线程外观用户界面线程外观图图14.5 从从CWinThread派生用户界面线程窗口外观派生用户界面线程窗口外观 图图14.6 用户界面线程演示主窗口用户界面线程演示主窗口 图图14.7 用户界面线程窗口用户界面线程窗口14.3 线
16、程的终止线程的终止 一般情况下,线程执行完成后就自动一般情况下,线程执行完成后就自动结束即正常终止。结束即正常终止。 如果线程没有执行完预定任务或需要如果线程没有执行完预定任务或需要由外部某种事件强制结束称为异常终止。由外部某种事件强制结束称为异常终止。 线程除正常执行完毕外,在调用了线程除正常执行完毕外,在调用了ExitThread()函数、函数、ExitProcess()函数、函数、TerminateThread()函数和函数和TerminateProcess()函数后都可以终止线函数后都可以终止线程的运行。程的运行。 由由MFC创建的进程也可以由这些函数创建的进程也可以由这些函数终止运行
17、。终止运行。14.3.1 14.3.1 线程的正常终止线程的正常终止 通常,线程执行到线程函数的结尾时,通常,线程执行到线程函数的结尾时,系统会自动调用系统会自动调用Win32 API函数函数ExitThread()终止该线程,用户不需要干预终止该线程,用户不需要干预就可以结束一个线程,但如果某些条件需就可以结束一个线程,但如果某些条件需要线程退出运行,就需要编程调用函数要线程退出运行,就需要编程调用函数ExitThread()。 该函数原型如下:该函数原型如下: VOID ExitThread(DWORD dwExitCode) 14.3.2 14.3.2 线程的异常终止线程的异常终止 当线
18、程没有运行结束时强行终止线程当线程没有运行结束时强行终止线程的执行称为异常终止,异常终止需要调用的执行称为异常终止,异常终止需要调用Win32 API函数函数TerminateThread()终止线终止线程的运行。程的运行。 该函数原型如下:该函数原型如下:BOOL TerminateThread(HANDLE hThread,CDWORD dwExitCode)14.4 线程的优先级与管理线程的优先级与管理 在多任务操作系统,如在多任务操作系统,如Windows、UNIX中,将进程和线程按其性质分为不同中,将进程和线程按其性质分为不同类型,然后根据各自的类型,分别赋予不类型,然后根据各自的类
19、型,分别赋予不同的优先级,方便系统的调度和控制。同的优先级,方便系统的调度和控制。 14.4.1 14.4.1 线程的优先级线程的优先级 如前所述,同一进程中的各个线程共如前所述,同一进程中的各个线程共同分享进程的同分享进程的CPU资源。资源。 CPU时间的分配是由系统进程(线程)时间的分配是由系统进程(线程)调度程序控制的。调度程序控制的。 调度的一个基本原则是根据进程的优调度的一个基本原则是根据进程的优先级进行时间片分配。先级进行时间片分配。 线程也有自己的优先级,线程的优先线程也有自己的优先级,线程的优先级取决与两个方面的因素:线程所在进程级取决与两个方面的因素:线程所在进程的优先级和线
20、程本身的优先级。的优先级和线程本身的优先级。 也就是说,线程的实际优先级是由上也就是说,线程的实际优先级是由上述两个优先级组合成的优先级,即基本优述两个优先级组合成的优先级,即基本优先级确定的。先级确定的。 其中,进程的优先级分为其中,进程的优先级分为4个等级:个等级: (1)IDLE_PRIORITY_CLASS (2)NORMAL_ PRIORITY_CLASS (3)HIGH_PRIORITY_CLASS (4)REALTIME_ PRIORITY_CLASS 这这4个等级的优先级顺序依次增加,即个等级的优先级顺序依次增加,即REALTIME_ PRIORITY_CLASS的优先级的优先
21、级最高。最高。 线程的优先级则是以进程的优先级为线程的优先级则是以进程的优先级为基础,通过进程的优先级和线程的优先级基础,通过进程的优先级和线程的优先级计算出线程的基本优先级。计算出线程的基本优先级。 线程的优先级分为线程的优先级分为7个等级:个等级: (1)THREAD_PRIORITY_IDLE (2)THREAD_PRIORITY_LOWEST (3)THREAD_PRIORITY_BELOW_NORMAL (4)THREAD_PRIORITY_ NORMAL (5)THREAD_PRIORITY_ABOVE_NORMAL (6)THREAD_PRIORITY_HIGHEST (7)TH
22、READ_PRIORITY_TIME_CRITICAL 这这7个等级的优先级依次提高,个等级的优先级依次提高,THREAD_PRIORITY_TIME_CRITICAL的的优先级最高。优先级最高。 14.4.2 14.4.2 线程的优先级管理线程的优先级管理 从从14.2节创建线程的过程可以看出,节创建线程的过程可以看出,无论创建的是工作者线程还是用户界面线无论创建的是工作者线程还是用户界面线程,每个线程在创建时都有一个优先级程,每个线程在创建时都有一个优先级(实例采用默认优先级(实例采用默认优先级THREAD_PRIORITY_ NORMAL)。)。 线程创建后的优先级可以根据实际情线程创建
23、后的优先级可以根据实际情况改变。况改变。 MFC中中CWinThread类提供的方法就类提供的方法就可以设置和访问线程的优先级。可以设置和访问线程的优先级。1 1获取线程的优先级获取线程的优先级 CWinThread类获取线程优先级的函类获取线程优先级的函数原型如下:数原型如下: BOOL SetThreadPriority(int nPriority) 2 2设置线程的优先级设置线程的优先级 CWinThread类设置线程优先级的函类设置线程优先级的函数原型如下:数原型如下: int GetThreadPriority(void)14.4.3 14.4.3 线程的调度线程的调度 我们知道,线
24、程在一个生存周期中,我们知道,线程在一个生存周期中,总是处于就绪、运行或阻塞状态的某一个总是处于就绪、运行或阻塞状态的某一个状态之中。状态之中。 系统线程调度程序总是从线程就绪队系统线程调度程序总是从线程就绪队列中调度某个线程投入运行。列中调度某个线程投入运行。 如果就绪队列中有多个线程的级别相如果就绪队列中有多个线程的级别相同,系统就会为这些线程中的每个线程平同,系统就会为这些线程中的每个线程平均分配均分配CPU的时间;若这些线程中有级别的时间;若这些线程中有级别高的线程,系统就会分配高的线程,系统就会分配CPU时间片让高时间片让高级别的线程投入运行,只有级别高的线程级别的线程投入运行,只有
25、级别高的线程运行完以后,低级别的线程才能获得运行完以后,低级别的线程才能获得CPU的时间片投入运行。的时间片投入运行。 每一个线程运行时要占用一个每一个线程运行时要占用一个CPU,因此,单因此,单CPU的计算机系统只能有一个线的计算机系统只能有一个线程。程。 线程的优先级在线程的优先级在015的称为普通优先的称为普通优先级,是应用程序中线程经常使用的优先级。级,是应用程序中线程经常使用的优先级。 线程的优先级在线程的优先级在1531的统称为实时的统称为实时优先级,它与普通优先级的最大区别是相优先级,它与普通优先级的最大区别是相同优先级的线程运行不轮流占用同优先级的线程运行不轮流占用CPU时间时
26、间片,而是先运行的线程先控制片,而是先运行的线程先控制CPU,如果,如果它不主动放弃对它不主动放弃对CPU的控制,同级或低级的控制,同级或低级的线程就无法得到运行。的线程就无法得到运行。 为了使低级别的线程也能够执行,只为了使低级别的线程也能够执行,只能将高级别线程挂起。能将高级别线程挂起。 这是因为这是因为Windows是一个强占式多任是一个强占式多任务的操作系统,即任何时候系统都可以中务的操作系统,即任何时候系统都可以中断一个线程,强迫这个线程放弃断一个线程,强迫这个线程放弃CPU的使的使用权,并把用权,并把CPU的使用权交给另一个线程。的使用权交给另一个线程。 在在Visual C+6.
27、0开发工具包中,提供开发工具包中,提供了一个用于观察系统中运行的进程和线程了一个用于观察系统中运行的进程和线程情况的实用工具情况的实用工具Process Viewer。 图图14.8 使用使用Process Viewer浏览浏览Exam14-2实例的线程实例的线程14.5 线程之间的通信线程之间的通信 我们知道,应用程序中创建的工作线我们知道,应用程序中创建的工作线程总是为主线程执行某种特定的任务。程总是为主线程执行某种特定的任务。 这样,主线程和工作线程之间必须建这样,主线程和工作线程之间必须建立一种信息传递机制,也就是线程间的通立一种信息传递机制,也就是线程间的通信。信。 线程间的通信方式
28、主要有全局变量方线程间的通信方式主要有全局变量方式和用户自定义消息方式。式和用户自定义消息方式。 下面讨论这两种方式是如何实现线程下面讨论这两种方式是如何实现线程间通信的。间通信的。14.5.1 14.5.1 通信机制通信机制 采用全局变量的方式进行线程间通信采用全局变量的方式进行线程间通信的机制非常简单,因为全局变量在应用程的机制非常简单,因为全局变量在应用程序中,任何一个对象都可以访问它。序中,任何一个对象都可以访问它。 线程隶属于应用程序的进程,必然可线程隶属于应用程序的进程,必然可以访问系统的全局变量。以访问系统的全局变量。 用户自定义消息方式采用的是用户自定义消息方式采用的是Wind
29、ows的消息驱动机制。的消息驱动机制。 工作者线程没有消息循环,不具备接工作者线程没有消息循环,不具备接收消息的条件,那么,主线程就不能以发收消息的条件,那么,主线程就不能以发送消息的方式与工作者线程进行通信。送消息的方式与工作者线程进行通信。 用户界面线程是具有消息循环的,因用户界面线程是具有消息循环的,因此,主线程和用户界面线程能以发送消息此,主线程和用户界面线程能以发送消息的方式进行双向通信。的方式进行双向通信。 不过,需要各自定义自己的消息处理不过,需要各自定义自己的消息处理函数。函数。 14.5.2 14.5.2 工作者线程通信工作者线程通信 【例例14-3】采用全局变量与工作者采用
30、全局变量与工作者线程通信,具体步骤如下:线程通信,具体步骤如下:图图14.9 工作者线程采用全局变量通信设计阶段界面工作者线程采用全局变量通信设计阶段界面 图图14.10 启动线程后的界面启动线程后的界面 图图14.11 停止线程后的界面停止线程后的界面14.5.3 14.5.3 用户界面线程通信用户界面线程通信 全局变量在线程通信中的应用多是在全局变量在线程通信中的应用多是在主线程对子线程的控制上,而从子线程向主线程对子线程的控制上,而从子线程向主线程的信息传递多采用自定义消息的方主线程的信息传递多采用自定义消息的方式进行。式进行。 由于用户界面线程和主线程各自都有由于用户界面线程和主线程各
31、自都有自己的消息循环,所以二者之间可以采用自己的消息循环,所以二者之间可以采用自定义消息方式相互通信。自定义消息方式相互通信。 【例例14-4】用户界面线程中的自定用户界面线程中的自定义消息方式通信。具体步骤如下:义消息方式通信。具体步骤如下: 图图14.12 自定义消息通信设计阶段界面自定义消息通信设计阶段界面 图图14.13 用户界面线程对话框设计阶段界面用户界面线程对话框设计阶段界面 图图14.14 自定义消息通信主界面自定义消息通信主界面 图图14.15 用户界面线程运行外观用户界面线程运行外观 图图14.16 用户界面线程填充进度条用户界面线程填充进度条 图图14.17 接收到用户界
32、面线程的消息接收到用户界面线程的消息14.6 线程的同步线程的同步 通常我们说的通常我们说的“同步同步”,是指同时做某,是指同时做某一件事情,即统一步伐。一件事情,即统一步伐。 线程的同步是指同一个进程中的多个线程的同步是指同一个进程中的多个线程能够协调一致的工作。线程能够协调一致的工作。 当某种资源在任一时刻只允许一个线当某种资源在任一时刻只允许一个线程访问时,若另有线程也要访问这个资源程访问时,若另有线程也要访问这个资源就会发生冲突;或当某种资源在任一时刻就会发生冲突;或当某种资源在任一时刻只允许只允许N个线程访问,而多于个线程访问,而多于N个线程访问个线程访问时就会发生冲突。时就会发生冲
33、突。 线程的同步就是为避免访问系统资源线程的同步就是为避免访问系统资源时发生冲突而提出来的。时发生冲突而提出来的。 所谓同步,实际上就是将可能要发生所谓同步,实际上就是将可能要发生冲突的线程阻塞,待不冲突时再将阻塞的冲突的线程阻塞,待不冲突时再将阻塞的线程投入运行。线程投入运行。 14.6.1 14.6.1 同步对象同步对象 MFC提供了提供了4种同步对象用于协调线种同步对象用于协调线程间的同步问题。程间的同步问题。 这这4种同步对象是事件对象种同步对象是事件对象CEvent、互持对象互持对象CMutex、信号量对象、信号量对象CSemaphore和临界区对象和临界区对象CCriticalSe
34、ction,他们的基类都是,他们的基类都是CSyncObject。 下面简要介绍这下面简要介绍这4个对象的构造函数和个对象的构造函数和基本用法。基本用法。 1 1事件对象事件对象CEventCEvent CEvent对象的构造函数原型如下:对象的构造函数原型如下: CEvent(BOOL bInitiallyOwn = FALSE, BOOL bManualReset = FALSE, LPCTSTR lpszName = NULL, LPSECURITY_ATTRIBUTES lpsaAttribute = NULL ) 2 2信号量对象信号量对象CSemaphoreCSemaphore C
35、Semaphore对象的构造函数如下:对象的构造函数如下: CSemaphore( LONG lInitialCount = 1, LONG lMaxCount = 1, LPCTSTR pstrName = NULL, LPSECURITY_ATTRIBUTES lpsaAttributes = NULL ) 3 3临界区对象临界区对象CCriticalSectionCCriticalSection和互斥对象和互斥对象CMutexCMutex CCriticalSection对象的构造函数原型对象的构造函数原型比较简单:比较简单: CCriticalSection( ) CMutex对象的构
36、造函数原型:对象的构造函数原型: CMutex( BOOL bInitiallyOwn = FALSE, LPCTSTR lpszName = NULL, LPSECURITY_ATTRIBUTES lpsaAttribute = NULL );14.6.2 14.6.2 使用事件对象使用事件对象 使用事件对象是实现线程同步的最简使用事件对象是实现线程同步的最简单办法。单办法。 当线程访问某一资源前,需要等待某当线程访问某一资源前,需要等待某一事件发生时,可使用事件对象进行同步。一事件发生时,可使用事件对象进行同步。 例如,当通信端口缓冲区接收到数据例如,当通信端口缓冲区接收到数据时,访问缓冲
37、区的线程才能被激活等。时,访问缓冲区的线程才能被激活等。 下面用一个实例说明事件对象的使用下面用一个实例说明事件对象的使用方法。方法。 【例例14-5】使用事件同步对象,用使用事件同步对象,用两个线程向编辑框写数据,且一个线程访两个线程向编辑框写数据,且一个线程访问完成后再由另一个线程访问。问完成后再由另一个线程访问。 图图14.18 事件同步对象实例设计阶段界面事件同步对象实例设计阶段界面 图图14.19 未使用事件同步对象的输出未使用事件同步对象的输出 图图14.20 使用事件同步对象的输出结果使用事件同步对象的输出结果14.6.3 14.6.3 使用互斥对象使用互斥对象 系统中某些物理设
38、备或程序中的变量、系统中某些物理设备或程序中的变量、数据、表格、队列等共享资源,不允许两数据、表格、队列等共享资源,不允许两个线程同时使用,只能是一个线程用完了,个线程同时使用,只能是一个线程用完了,另一个线程才可以用,这种现象称为互斥。另一个线程才可以用,这种现象称为互斥。 保证互斥的基本模型是一个线程在使保证互斥的基本模型是一个线程在使用资源之前必须发出对这个资源的使用请用资源之前必须发出对这个资源的使用请求,称为加锁;在使用完之后释放这个资求,称为加锁;在使用完之后释放这个资源,称为解锁。源,称为解锁。 也就是说一个线程使用资源的顺序是也就是说一个线程使用资源的顺序是加锁、使用和释放。加
39、锁、使用和释放。 下面以实例说明互斥对象的使用方法。下面以实例说明互斥对象的使用方法。 【例例14-6】使用使用MFC的互斥同步对的互斥同步对象完成例象完成例14-5的任务。的任务。 图图14.21 使用互斥同步对象实例设计阶段界面使用互斥同步对象实例设计阶段界面 图图14.22 未使用互斥同步运行效果未使用互斥同步运行效果 图图14.23 使用互斥同步运行效果使用互斥同步运行效果14.6.4 14.6.4 使用临界区对象使用临界区对象 操作系统将一次仅能让一个线程使用操作系统将一次仅能让一个线程使用的资源称为临界资源,如一些输入输出的的资源称为临界资源,如一些输入输出的硬件设备和共享的数据资
40、源等都属于临界硬件设备和共享的数据资源等都属于临界资源。资源。 也就是说,临界资源不允许两个或两也就是说,临界资源不允许两个或两个以上的线程同时访问。个以上的线程同时访问。 MFC的临界区同步对象和互斥同步对的临界区同步对象和互斥同步对象很类似,也就是说使用临界区对象时一象很类似,也就是说使用临界区对象时一个线程只能拥有一个临界区对象,而另一个线程只能拥有一个临界区对象,而另一个线程使用临界区对象只有等这个线程释个线程使用临界区对象只有等这个线程释放了临界区对象才可以使用。放了临界区对象才可以使用。 14.6.5 14.6.5 使用信号量对象使用信号量对象 临界区同步对象和互斥同步对象,对临界
41、区同步对象和互斥同步对象,对共享资源的访问在任一时刻只允许一个线共享资源的访问在任一时刻只允许一个线程进行,而信号量同步对象是限制同一资程进行,而信号量同步对象是限制同一资源在这一刻访问线程的数量。源在这一刻访问线程的数量。 也就是说,某一资源可以允许同时有也就是说,某一资源可以允许同时有N个线程访问,而第个线程访问,而第N+1以后的线程只能挂起以后的线程只能挂起处于等待状态,当处于等待状态,当N个线程的某一个线程访个线程的某一个线程访问结束后,第问结束后,第N+1个线程立即投入运行,这个线程立即投入运行,这就是信号量同步对象的工作原理。就是信号量同步对象的工作原理。 【例例14-7】使用信号
42、量同步对象控使用信号量同步对象控制线程对资源的访问数量。制线程对资源的访问数量。 图图14.24 使用信号量同步对象实例外观使用信号量同步对象实例外观 图图14.25 信号量同步对象运行界面图信号量同步对象运行界面图 图图14.26 设定信号量同步和启动线程数量设定信号量同步和启动线程数量小结小结 本章在阐述进程和线程的基础上,以本章在阐述进程和线程的基础上,以具体的应用实例,主要讨论了具体的应用实例,主要讨论了MFC类库对类库对多线程程序设计的支持。多线程程序设计的支持。 概括起来有以下几个方面:概括起来有以下几个方面: (1)线程由创建到消失有一个生存周)线程由创建到消失有一个生存周期,分
43、为创建、挂起、运行、终止,并且期,分为创建、挂起、运行、终止,并且有相应的函数支持这些操作。有相应的函数支持这些操作。 由由MFC创建的线程可以使用创建的线程可以使用Win32 API函数终止、挂起或恢复运行。函数终止、挂起或恢复运行。 (2)MFC线程分为工作者线程和用户线程分为工作者线程和用户界面线程。界面线程。 工作者线程用于处理费时或不需用户工作者线程用于处理费时或不需用户干预的数据处理工作,而用户界面线程主干预的数据处理工作,而用户界面线程主要是为了响应用户输入,处理系统发送的要是为了响应用户输入,处理系统发送的消息和事件,后者具有消息队列和消息循消息和事件,后者具有消息队列和消息循
44、环。环。 (3)线程间的通信可采用全局变量和)线程间的通信可采用全局变量和自定义消息方式。自定义消息方式。 工作者线程和用户界面线程都可以采工作者线程和用户界面线程都可以采用全局变量和自定义消息方式进行通信。用全局变量和自定义消息方式进行通信。 工作者线程和用户界面线程可以发送工作者线程和用户界面线程可以发送消息到主线程,但工作者线程不能接收消消息到主线程,但工作者线程不能接收消息,因为工作者线程没有消息循环。息,因为工作者线程没有消息循环。 (4)线程的优先级是由线程所在进程)线程的优先级是由线程所在进程的优先级和线程本身的优先级确定的。的优先级和线程本身的优先级确定的。 进程优先级分为进程
45、优先级分为4个等级,线程的优先个等级,线程的优先级分为级分为7个等级,二者的组合构成线程的基个等级,二者的组合构成线程的基本优先级,分为本优先级,分为031共共32个等级,这个优个等级,这个优先级就是线程运行时所具有的优先级。先级就是线程运行时所具有的优先级。 线程的优先级可以通过线程的优先级可以通过Windows API或或CWinThread提供的函数进行访问。提供的函数进行访问。 (5) MFC提供了提供了4种同步对象,即事种同步对象,即事件对象件对象CEvent、互斥对象、互斥对象CMutex、临界、临界区对象区对象CCriticalsection和信号量对象和信号量对象CSemaph
46、ore,实现线程对象对访问资源,实现线程对象对访问资源的控制。的控制。 除信号量同步对象是控制同一时刻访除信号量同步对象是控制同一时刻访问资源的线程数量外,其他同步对象的目问资源的线程数量外,其他同步对象的目的是在同一时刻只能有一个线程对某种资的是在同一时刻只能有一个线程对某种资源进行访问。源进行访问。上机指导上机指导 实验一:工作者线程的设计和实现实验一:工作者线程的设计和实现 实验内容实验内容 工作者线程是多线程程序设计中使用工作者线程是多线程程序设计中使用最多的一种程序设计方法。最多的一种程序设计方法。 实际中如果多线程使用得当,可以大实际中如果多线程使用得当,可以大大提高程序的执行效率
47、和性能。大提高程序的执行效率和性能。 本例编写一个工作者线程,并将主线本例编写一个工作者线程,并将主线程中的参数传递给子线程。程中的参数传递给子线程。 实验目的实验目的 掌握工作者线程函数的声明格式和程掌握工作者线程函数的声明格式和程序设计方法。序设计方法。 实现思路实现思路 仿照仿照14.2.2节的节的【例例14-1】,创建一个,创建一个基于基于MFC的工程,使用按钮或菜单的单击的工程,使用按钮或菜单的单击事件创建一个工作者线程,然后编写线程事件创建一个工作者线程,然后编写线程函数。函数。 注意这个函数是一个全局函数。注意这个函数是一个全局函数。 然后,编译运行程序,观察线程函数然后,编译运
48、行程序,观察线程函数是否启动,是否按照自己的要求动作。是否启动,是否按照自己的要求动作。 修改线程创建函数修改线程创建函数AfxBeginThread()入口参数入口参数LPVOID pParam的实际对象,在的实际对象,在线程函数中访问这个指针,理解和体会这线程函数中访问这个指针,理解和体会这个指针的作用。个指针的作用。 图图14.28是实验一设计阶段参考界面,是实验一设计阶段参考界面,图图14.29是运行结果界面:是运行结果界面: 图图14.28 实验一设计阶段参考界面实验一设计阶段参考界面 图图14.29 实验一运行结果界面实验一运行结果界面 下面是测试创建函数入口参数的代码:下面是测试
49、创建函数入口参数的代码: void CExperiment1Dlg:OnButton1() /AfxBeginThread(MyThread,this); CEdit *pEdit = (CEdit*)GetDlgItem(IDC_EDIT1); AfxBeginThread(MyThread,pEdit); UINT MyThread(LPVOID pParam) /CExperiment1Dlg *Dlg = (CExperiment1Dlg *)pParam; /CEdit *pEdit = (CEdit*)Dlg-GetDlgItem(IDC_EDIT1); CEdit *pEdit = (CEdit*)pParam; pEdit-SetWindowText(这是线程启动后发的消息!); return 0; 实验二:线程同步对象使用实验二:线程同步对象使用 实验内容实验内容 线程同步是多线程程序设计中一个重线程同步是多线程程序设计中一个重要的问题。要的问题。 这里采用事件同步对象这里采用事件同步对象CEvent 实现线实现线程的同步。程的同步。 实验目的实验目的 掌握同步对象掌握同步对
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 自助美甲店合作合同范本
- 高空作业安全打协议合同
- 消毒用品捐献协议书模板
- 浴场会所托管合同协议书
- 离婚前三年的财产协议书
- 物业零星工程施工协议书
- 自媒体运营团队合同范本
- 第三方协议护理网签合同
- 续签的合同上没竞业协议
- 糖果批发转让协议书模板
- 辽宁省鞍山市2024-2025学年八年级下学期期末质量检测语文试卷(含答案)
- 2025年老年教育课程设计:跨学科合作教学法的探索与成效报告
- 国际压力性损伤-溃疡预防和治疗临床指南(2025年版)解读
- 《论语》导读(复旦版)学习通超星期末考试答案章节答案2024年
- 2023北京市专职工会社会工作者招录笔试历年典型考题及考点剖析附答案带详解
- 齐鲁工业大学2025级上半年期末大学法理学题库
- 叉车检验检测报告
- DNF装备代码大全
- 基于Qt的俄罗斯方块的设计(共25页)
- 古建筑木构件油漆彩绘地仗施工技术分析
- 食堂投诉处理方案
评论
0/150
提交评论