第十六部分多线程处理教学课件.ppt
《第十六部分多线程处理教学课件.ppt》由会员分享,可在线阅读,更多相关《第十六部分多线程处理教学课件.ppt(69页珍藏版)》请在三一文库上搜索。
1、第十六章 多线程处理,本章的学习内容包括六个方面: 1 理解多线程处理概念。 2 理解多线程处理怎样提高程序性能。 3 理解如何创建、管理和销毁线程。 4 理解线程生命期。 5 理解线程同步。 6 理解线程优先级和调度。,多线程处理具有广泛的应用。例如,当进程从网上下载音频 或视频大文件时,不希望等整个文件下载完毕后才开始播放。 为了解决这个问题,可以设计两个线程。一个线程下载文件, 另一个线程负责播放。使得两种操作能同时进行。为避免播放 操作的时断时续,还应对两个线程进行 “同步”,在下载了足够 的数据后才开始播放。与此同时,下载数据仍在继续。 C+/CLI 的自动化 “垃圾回收” 也是通过
2、多线程实现的。即程序 运行的同时,自动化 “垃圾回收” 线程始终跟踪着指针所引用的 托管类对象的活动,并在其不再使用时,回收该对象所占用的 内存资源。,.NET Framework 类库允许开发者轻松地使用并发性指令,即 可以指定一个程序包含多个执行线程,每个线程都代表程序的 一个独立运行的流程,程序的各个流程都可以并发执行。,16.1 线程状态:线程生命期 对于托管类多线程应用程序,Thread 和 Monitor 类型是尤为重 要的。这两个类均在 System:Threading 命名空间中,Thread 用于 线程的创建、运行和辅助功能,Monitor 用于创建线程间的同步 机制。线程一
3、旦成功创建,在任何时刻都处于某种 “线程状态”。 线程的状态包括: unSatarted: “未启动” 状态 Running: “运行”状态 Stopped: “停止”状态 Blocked: “阻塞”状态 WaitSleepJoin: “等待-休眠-联接”状态 Suspended: “挂起”状态或“延迟执行”状态,线程的生命期(各种线程状态之间的转换)如下图所示:,Unstarted,Running,WaitSleepJoin,Suspended,Stopped,Blocked,Resume,Wait, Sleep, Join,Pulse, PulseAll, Interrupt, 被依赖线程
4、终止,Suspend,Abort, 线程完成,同步锁禁用, 发出I/O请求,同步锁可用, I/O操作完成,Start,1 Unstarted - Running:程序创建一个线程对象,并向构造函数 传递一个 ThreadStart 委托时,线程将从 Unstarted 开始其生命 期。ThreadStart 委托指定了线程在生命期内要进行的操作。 该委托必须是一个不接收任何参数的 void 类型方法。在没有 调用 Thread 对象的 Start 方法之前,线程始终处于 Unstarted 状态。一旦方法 Start 方法被调用,线程便进入 Running 状态。 2 Running - St
5、opped:发生这种状态转换的情况有两种: 线程在其委托 ThreadStart 终止后,便进入 Stopped 状态。 Thread 对象的 Abort 方法被调用 ,线程抛出一个线程异常 ThreadAbortException,导致线程终止。如果线程已经处于 Stopped 状态,并且程序中不再存在指向该线程的指针,,则垃圾回收器就能从内存中撤消该线程对象。 注意,从内部来说,调用线程的 Abort 方法时,线程实际 上先进入并保持 “AbortRequested” 状态,直至接收到线程 异常 ThreadAbortException,线程才进入 Stopped 状态。因 此,调用 Ab
6、ort 方法时,如果线程处于 WaitSleepJoin、 Suspended 或者 Blocked 状态,线程就会暂时保持当前状态 以及 “AbortRequested” 状态,直到线程离开了当前状态,才 能接收到 ThreadAbortException。 3 Running Blocked:发生这种状态转换的情况有两种: 线程请求 I/O 等操作,使得该线程无法使用处理器(即使 处理器可用),便进入 Blocked 状态,即操作系统会一直,阻塞线程的执行,直到线程请求的 I/O 等操作完成后,线 程将恢复 Running 状态。 线程同步时,必须调用 Monitor 的 Enter 方法
7、,以便获得一 个对象的同步锁,如果该锁暂时被禁用(其他同步线程占 用),则该线程就会进入 Blocked 状态;直到同步锁可用 后,该线程才恢复 Running 状态。 4 Running WaitSleepJoin:发生这种状态转换的情况有三种: 线程遇到了暂时不能执行的代码(通常是因为不满足某个 执行条件),线程就可能调用 Monitor 的 Wait 方法,从而 进入 WaitSleepJoin 状态。之后,只有另一个线程调用了 Monitor 的 Pulse 或 PulseAll 方法,线程恢复 Running 状态。, 一个 Running 状态线程的 Sleep 方法可被调用,使线
8、程进 入 WaitSleepJoin 状态。休眠时间由传给 Sleep 方法的参数 指定。一旦休眠时间到期,该线程便恢复 Running 状态。 对于调用 Monitor 类型的 Wait 方法或 Thread 类型的 Sleep 方 法进入休眠状态的线程,只要正在休眠线程的 Interrupt 方 法被调用,就会使休眠线程离开 WaitSleepJoin 状态,恢复 Running状态 如果一个线程应在另一线程终止后才能继续执行,则称此 线程为依赖线程。 依赖线程需要调用另一线程的 Join 方 法来 “联接” 这两个线程。两个线程 “联接” 后,依赖线程便 进入 WaitSleepJoin
9、 状态。直到另一个线程结束,依赖线程 便脱离 WaitSleepJoin 状态,进入 Running 状态。,5 Running Suspended:一个 Running 状态线程的 Suspend 方法 被线程自身或另一线程调用之后,该线程就会被挂起,进入 Suspended 状态。只有被挂起线程的 Resume 方法被调用,被 挂起的线程才会恢复 Running 状态。在内部,调用 Running 状 态线程的 Suspend 方法后,线程首先进入 “SuspendRequested” 状态,再进入 Suspended 状态。在等待 Suspend 请求时,线程 将保持 “SuspendR
10、equested” 状态。因此,调用线程的 Suspend 方法时,如果线程正处于 WaitSleepJoin 或 Blocked 状态,则线 程将保持它当前的状态;直到线程离开了当前状态,才会响 应 Suspend 请求。,6 如果线程的属性 IsBackground 被设置为 true,则该线程就会 保持 Background 状态。线程可以同时处于 Background 状态 和其他任何一种状态。 结束一个进程的条件之一是必须等待所有 Foreground 线程( 不处于 Background 状态的线程)结束执行,或进入 Stopped 状态,否则进程将不能终止。但是如果一个进程中只剩
11、下 Background 线程,那么 CLR 可调用那些 Background 线程的 Abort 方法来终止它们,并使进程终止。,16.2 线程优先级和线程调度 每个线程都有优先级,优先的取值范围是由定义在命名空间 System:Threading 中的枚举 ThreadPriority 确定的。该枚举的成 员包括 Lowest、BelowNormal、Normal、AboveNormal 和 Highest。 每个线程的缺省优先级是 Normal。 Windows 平台支持 “时间分片” 的概念,它使优先级相同的线 程能共享一个处理器。这就是说,系统为优先级相同的线程每 次分配一段相同长度
12、的短暂的处理器时间,称为一个 “quantum” (量子时间)或者 “time slice”(时间片)。,线程只能在分配给自己的量子时间中才能执行。量子时间到 期后,即使线程尚未结束执行,处理器也会离开那个线程,改 为执行下一个具有相同优先级的线程。 线程调度器的职责是保证当前运行的总是具有最高优先级的 线程;如果有多个相同优先级的线程,就保证这些线程 “轮流” 执行固定的量子时间。只有相同优先级的线程全部执行结束, 具有较低优先级的线程才能得到用于执行的量子时间。下图描 述了优先级队列对线程执行的作用:,优先级 Highest,优先级 AboveNormal,优先级 Normal,优先级 B
13、elowNormal,优先级 Lowest,A,B,就绪线程,C,D,E,F,G,注意,高优先级的线程可能推迟低优先级的线程的执行,而 且推迟时间可能是不确定的,这种不确定的推迟被称为 “饥饿” (Starvation)。在 Windows 中,线程调度器会随着时间的推移, 增大一个 “饥饿” 线程的优先级,使该线程能够执行一个量子时 间。一旦该量子时间到期(而且线程还没有终止),线程又会 恢复它的原有优先级。如果线程继续遭受不确定的推迟,线程 调度器可能会重复上述操作。 线程会一直执行,直到遇到以下几种情况才会暂停或停止: 线程死亡状态; 线程因输入/输出(或其他原因)而进入阻塞状态;, 线
14、程的 Sleep 方法、Join 方法或者 Monitor 类型的 Wait 方法被 调用,从而进入等待状态; 线程被优先级更高的线程占先执行而进入暂停状态; 分配给线程的量子时间到期后进入的暂停或停止执行状态。 暂停执行的线程,一旦遇到以下情况就回恢复执行: 休眠的线程被唤醒; 正被阻塞的线程的输入/输出操作结束; 为处于等待状态的线程调用了 Monitor 类型的 Pulse 或 PulseAll 方法;, 当一个联接线程结束时,如果有一个优先级比当前正要执行 的依赖线程高的线程处于暂停状态,该线程就会抢先执行;否则依赖线程就会执行。 接下来我们将通过对一系列的简单多线程应用程序实例的分
15、析,掌握托管类多线程应用程序的设计、编程基础。 每一个实例,我们都只对程序的设计目的、设计思路、编程 资源、编程要点和运行结果进行分析。读者可以根据这些分析 对程序代码进行详细学习,在理解的基础上自行重复实例的设 计和实现,并提倡在模仿的基础上添加自己的设计意图和需求。,16.3 创建和执行线程 SimpleThreads 1 设计目的 演示基本的线程处理技术,如何构造一个 Thread 对象,以及 如何使用 Thread 类的静态方法 Sleep。,2 设计思路 根据设计目的,在程序中创建 3 个具有缺省优先级 Normal 的 线程。每个线程开始执行后都显示一条信息: “表明它将要进入休眠
16、过程(休眠时间为 0 5000 毫秒范围内 的随机数),然后立即进入休眠状态。” 每个线程被唤醒(休眠过程结束)时,都显示一条信息: “显示线程的名称,并表明该线程已经结束休眠,并将要进入 Stopped 状态。” 本例是一个托管控制台程序。,3 编程资源 创建线程对象的 Thread 类是在 System:Threading 命名空间中 定义的。该类的常用属性和方法如下: 常用属性: CurrentThread 静态属性,获取当前正在执行的线程对象 的引用 Thread。 IsBackground 动态属性,获取和设置线程的 Background (后台)属性。该属性为 true,指示线程为
17、 后台线程;否则为前台线程。 Name 动态属性,获取和设置线程名称。,Priority 动态属性,获取和设置线程的优先级。 常用方法: Abort 动态方法,终止线程。 Join 动态方法,阻塞线程,直到一个线程终止 执行。 Sleep 静态方法,阻塞线程,直到指定时间(毫 秒)结束。 Start 动态方法,启动线程按预定流程执行。 ToString 动态方法,返回一个描述线程的字符串。,4 编程要点 由于程序中使用了线程类 Thread,因此需要在程序中使用该类源代码文件中添加使用 Thread 类所属的命名空间语句: using namespace System:Threading; 本
18、例中的三个线程对象的创建和启动都是在主函数 main 中完 成的,而线程的操作是由自定义类 MessagePrinter 的 Print 方 法提供的。 MessagePrinter 和 MessagePrinter:Print 的定义如下: public ref class MessagePrinter public: MessagePrinter(void); void Print(void);,private: int sleepTime; static Random random = gcnew Random(); ; / end class MessagePrinter void M
19、essagePrinter:Print() / obtain pointer to currently executing thread Thread current = Thread:CurrentThread; / put thread to sleep for sleepTime amount of time Console:WriteLine(String:Concat(current-Name, L“ going to sleep for “, sleepTime.ToString(); Thread:Sleep(sleepTime);,/ print thread name Con
20、sole:WriteLine(L“0 done sleeping“, current-Name); / end method Print 其中线程的休眠时间 sleepTime 是在 MessagePrinter 类对象创建时,由构造函数确定的: MessagePrinter:MessagePrinter / pick random sleep time between 0 and 5 seconds sleepTime = random-Next( 5001 ); , 程序的主函数 main 的关键代码如下: int main( array args ) / Create and name
21、each thread. Use MessagePrinters / Print method as argument to ThreadStart delegate MessagePrinter printer1 = gcnew MessagePrinter(); Thread thread1 = gcnew Thread( gcnew ThreadStart( printer1, ,MessagePrinter printer3 = gcnew MessagePrinter(); Thread thread3 = gcnew Thread( gcnew ThreadStart( print
22、er3, / end main,5 运行结果,16.4 无线程同步的生产者/消费者关系 UnSynchronized 1 设计目的 在 “生产者/消费者”(Producer/Consumer)关系中,应用程序 的 “生产者” 部分生成数据,“消费者” 部分则使用数据。在多线 程的 “生产者/消费者” 关系中,“生产者线程” 调用一个 “生产方 法” 来生产数据,并将数据放到名为 “缓冲区” 的共享内存区域。 “消费者线程” 则调用一个 “消费方法” ,从缓冲区中读取数据。 如果正在等待放入下一批数据的生产者线程发现消费者线程 尚未从缓冲区读取上一批数据,生产者线程应该调用 Wait 方 法;否
23、则,消费者线程将无法看到上一批数据,导致应用程序,丢失那些数据。消费者线程读取了数据后,应调用 Pulse 方法, 使正在等待的生产者继续。 如果消费者线程发现缓冲区为空,或者判断出缓冲区中的数 据已被它读取过,消费者就应该调用 Wait 方法;否则,消费者 线程就会从缓冲区读入 “垃圾” 数据,或者重复处理以前的数据。 生产者线程将下一批数据放入缓冲区后,生产者应调用 Pulse 方法,使消费者线程继续。 上述的生产和消费过程是在生产者线程和消费者线程对共享 数据进行同步访问下完成的。如果不进行同步就会生产逻辑错 误。设计本例的目的就是要演示:由于不同步产生的逻辑错误 是怎样发生的。这样的逻
24、辑错误会引起什么样的后果。,2 设计思路 根据设计目的,我们将本例设计、编写成一个多线程实现共 享数据的无同步生产和消费的应用程序。 为此,程序中包含三个类: HoldIntegerUnsynchronized 提供共享数据缓冲区,和对缓冲区 的基本读写方法。 Producer 提供 “生产线程” 的 “生产方 法” 和执 行方法的必要数据。 Consumer 提供 “消费线程” 的 “消费方 法” 和执 行方法的必要数据。 本例也是一个托管控制台程序。,3 编程资源 同前节(略) 4 编程要点 由于程序中使用了线程类 Thread,因此需要在程序中使用该类源代码文件中添加使用 Thread
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 第十六 部分 多线程 处理 教学 课件
链接地址:https://www.31doc.com/p-2584837.html