Presentation is loading. Please wait.

Presentation is loading. Please wait.

第9章 多 线 程.

Similar presentations


Presentation on theme: "第9章 多 线 程."— Presentation transcript:

1 第9章 多 线 程

2 本章主要介绍Windows操作系统下多线程的基本概念、创建管理线程的方法,以及线程的同步问题。

3 同时,MFC对线程操作进行了封装,提供了支持线程操作的类库。
本章主要讨论这些技术,并且给出相应的实例。

4 本章主要内容如下: ● Windows下多线程的基本概念; ● 用户界面线程和工作者线程; ● 线程的管理操作; ● 线程的同步; ● 多线程编程实例。

5 多线程的基本概念 两种重要的线程 线程的操作
9.1 两种重要的线程 9.2 线程的操作 9.3 小 结 9.4

6 9.1 多线程的基本概念 在Win32下,一个应用程序由一个或多个进程组成。
9.1 多线程的基本概念 在Win32下,一个应用程序由一个或多个进程组成。 一个进程由一个或多个线程以及代码、数据和其他内存中的程序资源组成。 典型的程序资源包括打开的文件、信号量、动态分配的内存等。 线程在进程空间中执行。

7 线程是操作系统分配处理器时间的最小单位。
每个线程有自己的堆栈、CPU寄存器、以及程序入口。 每个线程共享所有处理器的资源。

8 进程中的每个线程都独立执行,不会影响该进程中的其他线程。
所有线程共享公共的资源。 因此必须采用信号量或者其他进程间通信方法来调整线程的工作。

9 9.2 两种重要的线程 Windows提供了两种线程,即用户界面线程和工作者线程。
9.2 两种重要的线程 Windows提供了两种线程,即用户界面线程和工作者线程。 用户界面线程通常用来处理消息循环、与用户交互,工作者线程用来处理后台的计算。 下面分别介绍这两种线程。

10 用户界面线程 每一个Windows应用程序都有一个主线程。 这里的用户界面线程是指用来和用户进行交互的线程。

11 接收用户传送的数据,并且做出响应。 用户界面线程通常包含自己的窗口,有自己的消息循环,独立于应用程序的其他部分。

12 创建一个用户界面线程需要首先继承线程类CWinThread,重载它的成员函数,如表9-1所示。
最后调用AfxBeginThread创建线程对象。

13 表9-1 需要重载的CWinThread的成员函数
函 数 名 称 作 用 InitInstance 线程的初始化,通常需要重载 ExitInstance 释放线程占用的资源,通常需要重载 OnIdle 空闲时间的处理,不一定重载 PreTranslateMessage 过滤消息,不一定重载 ProcessWndProcException 处理线程抛出的意外 Run 线程控制函数,通常不重载

14 工作者线程 工作者线程通常用来处理后台运行的任务。 在后台任务运行的同时,用户可进行其他操作,不必等待后台任务的结束。

15 例如,一个三维模型编辑软件,用户要对两个模型做布尔运算。
在进行计算的同时,用户希望可以观察两个模型,对模型进行旋转,缩放的操作。

16 再如文本浏览软件的打印功能,在打印文本的工程中,用户仍然会继续浏览文本内容。
这些都属于工作者线程。

17 创建一个工作者线程只需要两个步骤。 首先实现工作者线程的功能函数,然后启动线程即可。

18 可以调用Win32提供的API函数CreateThread创建一个线程,MFC对Win32的线程操作做了封装,也可以通过调用AfxBeginThread创建一个线程对象。
这些函数及其调用方法将在下一节详细介绍。

19 9.3 线程的操作 本节介绍Windows线程的操作方法,包括线程的创建、线程的管理、线程的同步、线程的终止等。

20 9.3.1 线程的创建 线程的创建方法有3种,分别介绍如下。
线程的创建 线程的创建方法有3种,分别介绍如下。 1.调用Win32API函数CreateThread和CreateRemoteThread (1)CreateThread (2)CreateRemoteThread

21 2.C运行库函数_beginthreadex
3.调用函数AfxBeginThread

22 2.Win32API函数TerminateThread 3.C运行库函数 4.函数AfxEndThread
线程的终止 1.调用Win32API函数ExitThread 2.Win32API函数TerminateThread 3.C运行库函数 4.函数AfxEndThread

23 线程的管理和操作 1.线程的挂起、继续和休眠 (1) 挂起。 (2)继续。 (3)休眠。

24 2.线程的优先级 (1)级别。

25 表9-2 进程优先级列表 优 先 级 优 先 级 值 ABOVE_NORMAL_PRIORITY_CLASS
表9-2 进程优先级列表 优 先 级 优 先 级 值 ABOVE_NORMAL_PRIORITY_CLASS 大于NORMAL_PRIORITY_CLASS, 小于HIGH_PRIORITY_CLASS BELOW_NORMAL_PRIORITY_CLASS 大于IDLE_PRIORITY_CLASS, 小于NORMAL_PRIORITY_CLASS

26 续表 优 先 级 优 先 级 值 HIGH_PRIORITY_CLASS 13 IDLE_PRIORITY_CLASS 4
优 先 级 优 先 级 值 HIGH_PRIORITY_CLASS 13 IDLE_PRIORITY_CLASS 4 NORMAL_PRIORITY_CLASS 9(前台)或7(后台) REALTIME_PRIORITY_CLASS 24

27 表9-3 线程优先级列表 优 先 级 级 别 THREAD_PRIORITY_ABOVE_NORMAL 比进程优先级高一级 THREAD_PRIORITY_BELOW_NORMAL 比进程优先级低一级 THREAD_PRIORITY_HIGHEST 比进程优先级高两级 THREAD_PRIORITY_LOWEST 比进程优先级低两级 THREAD_PRIORITY_NORMAL 与进程优先级相同 THREAD_PRIORITY_TIME_CRITICAL 把线程优先级设为15 THREAD_PRIORITY_IDLE 把线程优先级设为1

28 (2)优先级的设置和获取。

29 3.线程ID的判断 4.线程的切换 5.打开线程 6.线程函数ThreadProc 7.获得线程的时间信息

30 8.处理器相关操作 (1)SetThreadAffinityMask函数 (2)SetThreadIdealProcessor函数

31 线程间的通信 线程间的通信通常采用共享全局变量,共享存储区来实现。 因为所有的线程都可以访问这些资源。 主线程不能通过发送消息给辅助线程实现通信,但辅助线程可以通过发送自定义消息达到和主线程通信的目的。

32 本节将通过一个简单的实例,介绍使用共享存储区和自定义消息实现线程间通信的功能。
【例9-1】 线程之间通信实例。

33 (a) (b) 图9-1 程序运行界面

34 线程的同步 在多线程程序设计中,经常会出现两个或多个线程使用一个公共变量,或者多个线程共享一些公共存储区的情况。

35 凡是涉及共享资源的情况都有可能会引起程序的错误。
为了解决这些问题,Windows提供了大量线程的同步方法,例如,变量锁、临界区、信号量、事件对象、互斥对象等。

36 一个或两个变量的互锁操作是最简单的同步原语。 Win32提供了7个具有线程安全性的原子操作,具体介绍如下。
1.互锁操作 一个或两个变量的互锁操作是最简单的同步原语。 Win32提供了7个具有线程安全性的原子操作,具体介绍如下。 (1)InterlockedIncrement (2)InterlockedDecrement

37 (3)InterlockedExchange
(4)InterlockedExchangeAdd (5)InterlockedExchangePointer (6)InterlockedCompareExchange (7)InterlockedCompareExchange Pointer

38 临界区(Critical Section)是一段程序代码,在任何时候都只能被一个线程使用。
2.临界区 临界区(Critical Section)是一段程序代码,在任何时候都只能被一个线程使用。 如果有多个线程同时访问临界区,这时只能有一个线程进入,其他线程则等待,直到临界区被释放。

39 与其他同步方法不同的是,临界区只能在单个进程内使用。
使用临界区的时候要避免长时间锁住一份资源。

40 进入临界区后必须尽快地离开,释放资源。 如果是主线程(GUI线程)要进入一个没有被释放的临界区,将会出现错误。

41 (1)InitializeCriticalSection
(2)DeleteCriticalSection (3)EnterCriticalSection (4)LeaveCriticalSection (5)CcriticalSection

42 表9-4 CEvent类的成员函数 函 数 名 称 作 用 CCriticalSection
作 用 CCriticalSection 构造函数,构造CCriticalSection对象 Lock 进入临界区 UnLock 离开临界区

43 事件(Event)是由Windows操作系统管理的同步对象,可以用于进程或线程的同步。
3.事件 事件(Event)是由Windows操作系统管理的同步对象,可以用于进程或线程的同步。 一个事件被创建后,只有激发状态和未激发状态两种状态,也称为发信号状态和未发信号状态。

44 事件包括手动重置事件和自动重置事件两种类型。
手动重置事件被设置为激发状态后,会唤醒所有等待的线程,而且一直保持激发状态,直到程序重新把它设置为未激发状态。

45 自动重置事件被设置为激发状态后,会唤醒一个等待中的线程,然后自动恢复为未激发状态。
所以用自动重置事件来同步两个线程比较理想。

46 (1)CreateEvent (2)OpenEvent (3)SetEvent、ResetEvent和PulseEvent函数 (4)Cevent

47 表9-5 CEvent类的成员函数 函 数 名 称 作 用 CEvent 构造函数,构造CEvent对象 SetEvent
作 用 CEvent 构造函数,构造CEvent对象 SetEvent 启动事件对象,释放等待线程 PulseEvent 启动事件对象,释放等待线程,或者重置事件对象 为未激活状态 ResetEvent 设置事件对象为未激活状态 Unlock 释放事件对象

48 4.互斥器 互斥器(Mutex)的功能与临界区相似。区别在于互斥器所花费的时间比临界区多很多,但是互斥器是核心对象(后面介绍的Event和Semaphore也是核心对象),可以跨进程使用,而且等待一个被锁住的互斥器可以设定TIMEOUT,不会像临界区那样无法得知临界区的情况,一直等待。

49 Win32提供了创建互斥器CreateMutex(),打开互斥器OpenMutex(),释放互斥器ReleaseMutex()等操作。

50 Mutex的拥有权并非属于产生它的那个线程,而是属于最后对此Mutex进行等待操作(Wait ForSingleObject)并且尚未进行ReleaseMutex()操作的线程。

51 线程拥有Mutex就好像进入临界区一样,一次只能有一个线程拥有该Mutex。
如果一个拥有Mutex的线程在返回之前没有调用ReleaseMutex(),那么这个Mutex就被舍弃了。

52 当其他线程等待这个Mutex时,仍能返回,并得到一个WAIT_ABANDONED_0返回值,一个Mutex被舍弃是Mutex特有的功能。

53 (1)CreateMutex (2)OpenMutex (3)ReleaseMutex (4)CMutex

54 信号量(Semaphore)是最具历史的同步机制。 信号量是解决producer/consumer问题的关键要素。
5.信号量 信号量(Semaphore)是最具历史的同步机制。 信号量是解决producer/consumer问题的关键要素。 对应的MFC类是CSemaphore。 Win32函数CreateSemaphore()用来产生信号量。

55 Release Semaphore()用来解除锁定。
Semaphore的现值代表的意义是目前可用的资源数,如果Semaphore的现值为1,表示还有一个锁定动作可以成功。

56 如果现值为5,就表示还有5个锁定动作可以成功。
当调用Wait等函数要求锁定,如果Semaphore现值不为0,Wait马上返回,资源数减1。

57 当调用ReleaseSemaphore()资源数加1,当时不会超过初始设定的资源总数。

58 (1)CreateSemaphore (2)OpenSemaphore (3)ReleaseSemaphore (4)Csemaphore

59 小 结 本章主要介绍Windows操作系统下多线程的基本概念,如何创建和管理线程,以及线程的同步问题。

60 通过本章的介绍,读者可以看出,多线程程序设计通常比一般的单线程程序复杂,在程序设计过程中,一定要考虑清楚各线程的关系,避免出现死锁或不同步的现象。

61 另外需要注意现在大多数用户使用单CPU计算机,在这种机器上运行多线程程序,有时反而会降低系统的性能。
因此,在设计多线程应用程序时,应慎重选择,视具体情况加以处理,使应用程序获得最佳的性能。


Download ppt "第9章 多 线 程."

Similar presentations


Ads by Google