第十六部分多线程处理教学课件.ppt

上传人:本田雅阁 文档编号:3129542 上传时间:2019-07-14 格式:PPT 页数:69 大小:326.03KB
返回 下载 相关 举报
第十六部分多线程处理教学课件.ppt_第1页
第1页 / 共69页
第十六部分多线程处理教学课件.ppt_第2页
第2页 / 共69页
第十六部分多线程处理教学课件.ppt_第3页
第3页 / 共69页
第十六部分多线程处理教学课件.ppt_第4页
第4页 / 共69页
第十六部分多线程处理教学课件.ppt_第5页
第5页 / 共69页
点击查看更多>>
资源描述

《第十六部分多线程处理教学课件.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

25、类所属的命名空间语句: using namespace System:Threading; 在 HoldIntegerUnsynchronized 类中,包含一个私有数据成员: private: int buffer; / 作为共享数据的缓冲区 对缓冲区的读写操作是通过可访问属性 Buffer 向外提供的: property int Buffer int get(), Console:WriteLine( L“0 reads 1“, Thread:CurrentThread-Name, buffer.ToString() ); return buffer; void set( int valu

26、e ) Console:WriteLine( L“0 writes 1“, Thread:CurrentThread-Name, value.ToString() ); buffer = value; , 在 Producer 类的数据成员包含 “生产方法” 执行时必需的共享数据对象和用于生产延时的随机休眠时间: private: HoldIntegerUnsynchronized sharedLocation; Random randomSleepTime; 这两个数据成员在 Producer 类对象创建时,进行初始化。 Producer:Producer( HoldIntegerUnsyn

27、chronized shared, Random random ) sharedLocation = shared; randomSleepTime = random; ,“生产方法” 的操作定义如下: void Producer:Produce() / sleep for random intreval up to 3000 milliseconds / then set sharedLocations Buffer property for( int count = 1; count Next( 1, 3000 ) ); sharedLocation-Buffer = count; / e

28、nd for Console:WriteLine( L“0 done producing.nTerminating 0“, Thread:CurrentThread-Name ); / end method Produce, 在 Consumer 类的数据成员包含 “消费方法”执行执行时必需的共享数据对象和 用于消费延时的随机休眠时间: private: HoldIntegerUnsynchronized sharedLocation; Random randomSleepTime; 这两个数据成员在 Consumer 类对象创建时,进行初始化。 Consumer:Consumer( Hold

29、IntegerUnsynchronized shared, Random random ) sharedLocation = shared; randomSleepTime = random; ,“消费方法” 的操作定义如下: void Consumer:Consume() int sum = 0; / sleep for random intreval up to 3000 milliseconds / then add sharedLocations Buffer property value to sum for( int count = 1; count Next( 1, 3000 )

30、 ); sum += sharedLocation-Buffer; / end for Console:WriteLine(L“0 read values totaling: 1.nTerminating 0“, Thread:CurrentThread-Name, sum.ToString() ); / end method Consume, “生产线程” 和 “消费线程” 的创建与启动是在主函数 main 中 完成的。在两个线程创建之前,必须先建共享缓冲区、生产者和消费者对象。例如 “生产线程” 对象的创建和启动: HoldIntegerUnsynchronized holdInteger

31、 = gcnew HoldIntegerUnsynchronized(); Random random = gcnew Random(); Producer producer = gcnew Producer( holdInteger, random ); Thread producerThread = gcnew Thread( gcnew ThreadStart( producer, ,5 运行结果,分析一次执行结果: 消费者读入了一个并非生产者生产的错误数据 -1; 数据 1、2、3 顺序被生产者生产,并正确被消费者读入消 费; 消费者线程结束执行时,发现消费的数据总值不等于生产 者生产

32、的数据总值,因为第一次读入的数据是错误的; 数据 4 未被消费,因为已经没有数据的消费者。 注意,如果多次执行,则会发现每次运行的结果都可能是不 一样。,16.5 有线程同步的生产者/消费者关系 Synchronized 1 设计目的 线程之间的同步是调用 Monitor 类的几个静态方法:Enter、 Wait、Pulse/PulseAll 和 Exit 相互配合实现的。 本例的设计目的是学会在设计和编写多线程应用程序时,如 何正确地加入同步操作,实现不同线程访问共享数据时,能避 免逻辑错误。,2 设计思路 根据设计目的,我们在实例 UnSynchronized 的基础上增加对 共享缓冲区访

33、问的线程同步操作,使得实例 UnSynchronized 运 行时出现的逻辑错误和不可预测性得以避免。 为此,需要在共享缓冲区类 HoldIntegerSynchronized 的属性 接口中对缓冲区 buffer 的读写操作中增加线程同步控制。为了更 好地描述线程执行期间,线程的操作细节和共享缓冲区的状态 变化,需要为 HoldIntegerSynchronized 类添加一个状态显示方法。由于需要在主函数 main 中调用 HoldIntegerSynchronized 的状态 显示方法,因此主函数 main 需要增加少许相应的新代码。而 Producer 和 Consumer 类无须进行

34、任何修改。,3 编程资源 前面谈及的 Monitor 类的几个静态方法的作用如下: Enter 获取对指定线程对象的独占锁,被指定的线程对 象由参数传递。 Wait 释放指定线程独占的锁,阻塞该线程,直至再次 获取该线程的独占锁。被指定的线程对象由参数 传递。 Pulse 唤醒正处于被阻塞的线程队列中的一个线程继续 执行。,PulseAll 唤醒正处于被阻塞的线程队列中的所有线程继续 执行。 Exit 释放对指定线程对象的独占锁,被指定的线程对 象由参数传递。,4 编程要点 由于程序中使用了线程类 Thread,因此需要在程序中使用该类源代码文件中添加使用 Thread 类所属的命名空间语句:

35、 using namespace System:Threading; 增加对缓冲区访问的同步控制, HoldIntegerSynchronized 类中 应该添加一个数据成员: int occupiedBufferCount; 用于指示缓冲区占用状况。 共享缓冲区 buffer 的对外可访问属性 Buffer 的定义应该被修改 如下:,property int Buffer int get() Monitor:Enter( this ); if( occupiedBufferCount = 0 ) Console:WriteLine( L“0 tries to read.“, Thread:C

36、urrentThread-Name); DisplayState( String:Concat( L“Buffer empty. “, Thread:CurrentThread-Name, L“ waits.“ ) ); Monitor:Wait( this ); / end if,occupiedBufferCount-; DisplayState( String:Concat( Thread:CurrentThread-Name, L“ reads “, buffer.ToString() ) ); Monitor:Pulse( this ); int bufferCopy = buffe

37、r; Monitor:Exit( this ); return bufferCopy; void set( int value ) Monitor:Enter( this ); if( occupiedBufferCount = 1 ), Console:WriteLine(L“0 tries to write.“, Thread:CurrentThread-Name); DisplayState( String:Concat(L“Buffer full. “, Thread:CurrentThread-Name, L“ waits.“); Monitor:Wait( this ); / en

38、d if HoldIntegerSynchronized:buffer = value; occupiedBufferCount+; DisplayState( String:Concat( Thread:CurrentThread-Name, L“ writes “, buffer.ToString(); Monitor:Pulse( this ); Monitor:Exit( this );, 添加状态显示函数 DisplayState ,其操作代码定义如下: void HoldIntegerSynchronized:DisplayState( String operation ) Con

39、sole:WriteLine( L“0, -35 1, -9 2n“ , operation, buffer.ToString(), occupiedBufferCount.ToString(); / end method DisplayState occupiedBufferCount 注意,格式串 L“0, -35 1, -9 2n” 表示第一个数据 operation 的 显示域为 35 个字符宽度;第二数据 buffer 的显示域为 9 个字 符宽度;第三个数据 occupiedBufferCount 的显示域不定宽度。, 在主函数 main 中需要增加如下代码: int main(

40、array args ) / create shared object used by threads HoldIntegerSynchronized holdInteger = gcnew HoldIntegerSynchronized(); / Random object used by each thread Random random = gcnew Random(); / create Producer and Consumer objects Producer producer = gcnew Producer( holdInteger, random ); Consumer co

41、nsumer = gcnew Consumer( holdInteger, random ); / output column heads and initial buffer state Console:WriteLine( L“0, -351, -92n“, L“Operation“, L“Buffer“, L“Occupied Count“ );,holdInteger-DisplayState( L“Initial state“ ); / create threads for producer and consumer and set / delegates for each thre

42、ad Thread producerThread = gcnew Thread( gcnew ThreadStart( producer, / end main,5 运行结果,16.6 生产者/消费者关系:循环缓冲区 CircularBuffer 1 设计目的 分析实例 Synchronized 的运行结果,发现该程序虽然实现对 共享数据生产和消费的同步,避免了丢失数据、使用垃圾数据 等逻辑错误;但线程过多地处于等待状态,使得运行效率不高。 引起线程过多地处于等待状态的直接原因是线程执行的速度 不一致,无论的生产快于消费,还是消费快于生产,都会引起 较快的线程过多地处于等待状态。即使线程开始以

43、相同的速度 执行,但运行一段时间之后,线程之间的相对执行速度也可能 失去 “同步”。,由于操作系统、网络用户和其他组件之间存在着太多的交 互,这些原因最终会使线程工作于不同的速度。所以我们无法 保证异步并发线程的相对速度。 多个线程共享资源并以基本相同的速度工作时,为了尽可能 缩短等待时间,可以使用一个循环缓冲区(Circular Buffer),它 提供了额外的缓冲区,生产者可在其中放入值,而消费者可从 中获取那些值。假定缓冲区是一个数组,生产者和消费者从数 组头开始工作,任何线程一旦抵达数组尾,就回到数组的第一 个元素,并执行下一任务。循环往复使用共享缓冲区。,注意,假如生产者和消费者以不

44、同速度运行,就可能不适合使 用循环缓冲区。例如消费者总是比生产者快,那么只包含一个 数据内存空间的缓冲区就足够了,更多的内存纯属浪费。反之 如果生产者快,缓冲区就可能需要包含不限数量的数据内存空 间,以便存放不断生产的数据。 使用循环缓冲区的关键在于足够的、恰当的数量的额外单 元,以处理预料之中的 “额外” 生成的数据。例如,我们发现生 产者总是生成比消费者的使用能力多 3 倍的数据,就可以定义 至少包含 3 个单元的缓冲区,以处理额外生成的数据。缓冲区 不能太小,否则线程会等待更长的时间。相反,也不能太大, 否则会浪费内存。,本例的设计目的是体会循环缓冲区对提高多线程应用程序的 运行效率的作

45、用,并初步学会为多线程应用程序设计合理的循 环缓冲区。,2 设计思路 根据设计目的,我们在前一节 Synchronized 实例的基础上, 将缓冲区修改为由数组实现的循环缓冲区。为了更友好地为用 户提供程序运行期间,线程执行的状态信息,本例将设计为一 个 Windows GUI 程序。 为此,需要对 HoldIntegerSynchronized 、Producer 和 Consumer 类进行相应的修改。HoldIntegerSynchronized 类需要包含五个数据 成员: array buffer; / 共享循环缓冲区 int occupiedBufferCount; / 被占用的缓冲

46、区计数,int readLocation; / 缓冲区的读取位置指针 int writeLocation; / 缓冲区的写入位置指针 TextBox outputTextBox; / 输出显示文本框控件 HoldIntegerSynchronized 类的属性接口 Buffer 的操作逻辑无须变 化,而只需要将信息文本的显示操作从控制台方式转到窗口控 件方式。另外,为了适应窗口程序的需要,定义一个产生操作 状态输出信息的方法 CreateStateOutput 替代实例 Synchronized 中 定义的方法 DisplayState。,对于 Producer 和 Consumer 类无须进

47、行许多修改,而只需要增 加一个输出文本框类 TextBox 的数据成员 outputTextBox 以及在 生产方法 Produce 和消费方法 Consume 中修改信息文本输出的方 式,以适应窗口应用程序的需要。 在实例 Synchronized 中,线程的创建和启动是在主函数 main 中完成的。而在一个窗口应用程序中,线程的创建和启动就应 该在程序主窗体创建时完成。,3 编程资源 同前节(略)。,4 编程要点 在 HoldIntegerSynchronized 类的头文件中需要添加如下使用某 些命名空间语句: using namespace System; using namespac

48、e System:Windows:Forms; using namespace System:Threading; 以便满足该类定义中使用系统中预定义类的需要。 在 HoldIntegerSynchronized 类的构造函数中必须完成所有类数 据成员的创建和初始化: HoldIntegerSynchronized:HoldIntegerSynchronized( TextBox output ): occupiedBufferCount( 0 ), readLocation( 0 ), writeLocation( 0 ), buffers = gcnew array(3); for(int i = 0; i Length; i+) buffersi = -1; outputTextBox = output; 输出信息产生方法 CreateStateOutput 的操作定义: String HoldIntegerSynchronized:CreateStateOutput() / display first line of state information String output = String:Concat( L“(buffers occupied: “, occupiedBuf

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

当前位置:首页 > 其他


经营许可证编号:宁ICP备18001539号-1