第18章多线程 主要内容 本章主要讲解多线程的编程,学习的目标包括:理解多线程 的概念;了解多线程提高程序性能的方式;掌握线程的创建、 控制和同步;理解线程的生命周期、优先级和调度;理解线 程的安全性;掌握线程的实现方式
第18章 多线程 主要内容 本章主要讲解多线程的编程,学习的目标包括:理解多线程 的概念;了解多线程提高程序性能的方式;掌握线程的创建、 控制和同步;理解线程的生命周期、优先级和调度;理解线 程的安全性;掌握线程的实现方式
18.1基本概念 ■181.1多线程的概念 程序是一段静态的代码,是应用软件执行的蓝本 进程是应用程序的一个运行例程,是应用程序的一次动态执 过程。进程由若干个代码和数据块组成,每个进程还拥有 其他的资源,如文件、动态内存地址、线程等 线程是进程中的一个执行单元。一个进程可以包含若干个线 程。同一进程中的各个线程对应于一组CPU指令、一组CPU 寄存器以及一个堆栈。进程并不执行代码,它只是代码存放 的地址空间。进程地址空间中所存放的代码由线程来执行。 多线程处理可以同时运行多个任务,因此多线程技术可以使 程序的响应速度更快,可以让占用大量处理时间的任务和当 前没有进行处理的任务定期将处理器时间让给别的任务,可 以随时停止任务,还可以设置每个任务的优先级以优化程序 性能
18.1 基本概念 ◼ 18.1.1 多线程的概念 程序是一段静态的代码,是应用软件执行的蓝本。 进程是应用程序的一个运行例程,是应用程序的一次动态执 行过程。进程由若干个代码和数据块组成,每个进程还拥有 其他的资源,如文件、动态内存地址、线程等。 线程是进程中的一个执行单元。一个进程可以包含若干个线 程。同一进程中的各个线程对应于一组CPU指令、一组CPU 寄存器以及一个堆栈。进程并不执行代码,它只是代码存放 的地址空间。进程地址空间中所存放的代码由线程来执行。 多线程处理可以同时运行多个任务,因此多线程技术可以使 程序的响应速度更快,可以让占用大量处理时间的任务和当 前没有进行处理的任务定期将处理器时间让给别的任务,可 以随时停止任务,还可以设置每个任务的优先级以优化程序 性能
18.1.2C#中的线程实现方法(1) 系统以对象方式来管理进程和线程,因此系统提供了线程对象。在C#中,实 现多线程有多种方法,最常见的三种方法是分别使用 Timer类(线程计数器) Thread Pool(线程池)类和 Thread(线程)类。 ■ Timer类 Timer类提供以指定的时间间隔执行方法的机制。该类是 sealed类,不能被继承 调用 Timer类的构造方法时,使用 TimerCallback委托指定希望 Timer执行的方法。 计时器委托在构造计时器时指定,并且不能更改。此方法不在创建计时器的线程 中执行,而是在系统提供的线程池线程中执行。创建计时器时,可以指定在第 次执行方法之前等待的时间量(截止时间)以及此后的执行期间等待的时间量 (时间周期)。可以使用 Change()方法更改这些值或禁用计时器。当不再需要 计时器时,可以使用 Dispose()方法释放计时器持有的资源 System. Threading Timer,是一个使用回调方法的计时器,而且由线程池线程服务, 简单且对资源要求不高。 Timer类适用于间隔性地完成任务
18.1.2 C#中的线程实现方法(1) 系统以对象方式来管理进程和线程,因此系统提供了线程对象。在C#中,实 现多线程有多种方法,最常见的三种方法是分别使用Timer类(线程计数器)、 ThreadPool(线程池)类和Thread(线程)类。 ◼ Timer类 Timer类提供以指定的时间间隔执行方法的机制。该类是sealed类,不能被继承。 调用Timer类的构造方法时,使用TimerCallback委托指定希望Timer执行的方法。 计时器委托在构造计时器时指定,并且不能更改。此方法不在创建计时器的线程 中执行,而是在系统提供的线程池线程中执行。创建计时器时,可以指定在第一 次执行方法之前等待的时间量(截止时间)以及此后的执行期间等待的时间量 (时间周期)。可以使用Change()方法更改这些值或禁用计时器。当不再需要 计时器时,可以使用Dispose()方法释放计时器持有的资源。 System.Threading.Timer是一个使用回调方法的计时器,而且由线程池线程服务, 简单且对资源要求不高。Timer类适用于间隔性地完成任务
18.1.2C#中的线程实现方法(2) ThreadPool类 Thread Pool为大多数任务提供最佳的基本线程创建和管理机制。该 类使用户能够请求某一线程执行任务而不必亲自完成任何线程管理 作 Thread Pool类提供一个线程池,该线程池可用于发送工作项、处理 异步IO、代表其他线程等待以及处理计时器。许多应用程序创建 的线程都要在休眠状态中消耗大量时间,以等待事件发生。其他线 程可能进入休眠状态,只被定期唤醒以轮询更改或更新状态信息。 线程池通过为应用程序提供一个由系统管理的辅助线程池让用户更 有效地使用线程。一个线程监视排到线程池的若干个等待操作的状 个等待操作完成时,线程池中的一个辅助线程就会执行对 应的回调函数
18.1.2 C#中的线程实现方法(2) ◼ ThreadPool类 ThreadPool为大多数任务提供最佳的基本线程创建和管理机制。该 类使用户能够请求某一线程执行任务而不必亲自完成任何线程管理 工作。 ThreadPool类提供一个线程池,该线程池可用于发送工作项、处理 异步 I/O、代表其他线程等待以及处理计时器。许多应用程序创建 的线程都要在休眠状态中消耗大量时间,以等待事件发生。其他线 程可能进入休眠状态,只被定期唤醒以轮询更改或更新状态信息。 线程池通过为应用程序提供一个由系统管理的辅助线程池让用户更 有效地使用线程。一个线程监视排到线程池的若干个等待操作的状 态。当一个等待操作完成时,线程池中的一个辅助线程就会执行对 应的回调函数
18.1.2C#中的线程实现方法(③3) Thread类 Timer类适用于间隔性地完成任务 Thread Pool类适用于多个小的线程 Thread类是实现线程的主要方法,它适用于各种情况
18.1.2 C#中的线程实现方法(3) ◼ Thread类 Timer类适用于间隔性地完成任务。 ThreadPool类适用于多个小的线程。 Thread类是实现线程的主要方法,它适用于各种情况
82 Thread类 Thread类■线程的状态和生命周期 程创行语在 中运共 BackGround nded Requested Unstarted A的Gom每图 应Su 调 B调用wa()Jon() A调用slep() 用 B调用 Interrupt() Aborto 元 图181线程的生命周期示意图
8.2 Thread类 ◼ Thread类 ◼ 线程的状态和生命周期 在 公 共 语 言 运 行 库 中 创 建 线 程 A A响 应Su spend 请求 SuspendedRequested Suspended BackGround WaitSleepJoin Unstarted Abort AbortRequested Stopped StoppedRequested A 调 用 start( ) B调用Interrupt( ) 设 ou 置 A的 Is 属 Ba ck nd 性 Gr A调用sleep( ) B调用wait( ), Join( ) B调 用Re sume ( ) 线 完 程 成 B 用 调 u s S e nd ( ) p 响 应 请 Abort 求 A B 调 Abort( ) 用 Running B调 用Re sume ( ) 图18.1 线程的生命周期示意图
18.3多线程编程的基本步骤(1) 第一个步骤是定义 Thread对象。 Thread类是一个 sealed类,不能被继 承,可以创建 Thread类的实例。每个线程指代一个方法,称为线程 方法或线程函数,该方法无参数,且返回值类型为void。若需要传 递相关的参数,可以使用对象的成员变量或方法。生成 Thread实例 时,首先定义一个指代,然后定义一个 Thread对象,调用 Thread类 的构造方法生成该实例。例如,下列代码生成一个 Thread实例: public delegate void ThreadStart () Thread thread 1=new Thread( new ThreadStart( obj. my ThreadFun )) private void my ThreadFun
18.3 多线程编程的基本步骤(1) 第一个步骤是定义Thread对象。Thread类是一个sealed类,不能被继 承,可以创建Thread类的实例。每个线程指代一个方法,称为线程 方法或线程函数,该方法无参数,且返回值类型为void。若需要传 递相关的参数,可以使用对象的成员变量或方法。生成Thread实例 时,首先定义一个指代,然后定义一个Thread对象,调用Thread类 的构造方法生成该实例。例如,下列代码生成一个Thread实例: public delegate void ThreadStart ( ); Thread thread1 = new Thread( new ThreadStart( obj.myThreadFun ) ); private void myThreadFun ( ) { ……; }
18.3多线程编程的基本步骤(2 第二个步骤是启动刚定义的线程 ethread 1,代码为: thread1 Start() 接着,根据具体情况,可以悬挂该线程,可以重启该线程,也可以 让该线程休眠等 最后,该线程执行完以后自动撤销。如果在线程执行完以前希望终 止它,则可以调用 abort()方法。注意,终止一个线程可能造成数据 的不完整等问题,因此调用该函数要特别当心
18.3 多线程编程的基本步骤(2) 第二个步骤是启动刚定义的线程thread1,代码为: thread1.Start( ); 接着,根据具体情况,可以悬挂该线程,可以重启该线程,也可以 让该线程休眠等。 最后,该线程执行完以后自动撤销。如果在线程执行完以前希望终 止它,则可以调用Abort( )方法。注意,终止一个线程可能造成数据 的不完整等问题,因此调用该函数要特别当心
184多线程的同步(1) ■184.1同步的方法 同步上下文。可以使用任何 Context Boundobject上的 SynchronizationAttribute,同步所有实例方法和字段。相同上下文域 中的所有对象共享相同的锁。允许多个线程访问方法和字段,但在 任一时刻只允许一个线程访问。 同步属性。 Hashtable和 Queue等几个类提供了一个可为该类的 实例返回线程安全包装的 Synchronized属性。 ●手动同步。可以使用各种同步对象来创建自己的同步机制。可以 使用同步类 Interlocked、 Monitor、 Reader Writerlock、 Manualresetevent和 Auto resetevent来获取锁和释放锁以保护全局、 静态和实例字段以及全局、静态和实例方法
18.4 多线程的同步 (1) ◼ 18.4.1 同步的方法 ⚫ 同步上下文。可以使用任何ContextBoundObject上的 SynchronizationAttribute来同步所有实例方法和字段。相同上下文域 中的所有对象共享相同的锁。允许多个线程访问方法和字段,但在 任一时刻只允许一个线程访问。 ⚫ 同步属性。Hashtable 和 Queue 等几个类提供了一个可为该类的 实例返回线程安全包装的Synchronized属性。 ⚫ 手动同步。可以使用各种同步对象来创建自己的同步机制。可以 使用同步类 Interlocked、Monitor、ReaderWriterLock、 ManualResetEvent 和 AutoResetEvent 来获取锁和释放锁以保护全局、 静态和实例字段以及全局、静态和实例方法
184多线程的同步(2) ■184.1同步的方法 同步代码区域。可以使用 Monitor类或编译器关键字来同步代码 块、实例方法和静态方法(不支持同步静态字段),从而改善性能。 C#支持使用特定的语言关键字来标记代码块,最终生成的代码将 尝试在代码执行时获取锁,如果已经获取锁,则正在执行的代码将 直等待,直到锁可用为止。当代码退岀同步代码块时,锁被释放 还可以用效果等价于 Monitor或一个用于 Monitor代码块的编译器关 键字的 MethodImplattribute'修饰方法并传递 MethodImplOptions Synchronized。 Thread. Interrupt可用于使线程跳 出阻塞操作(如等待访问同步代码区域),还用于使线程跳出 Thread. Sleep等操作
18.4 多线程的同步 (2) ◼ 18.4.1 同步的方法 ⚫ 同步代码区域。可以使用Monitor类或编译器关键字来同步代码 块、实例方法和静态方法(不支持同步静态字段),从而改善性能。 C# 支持使用特定的语言关键字来标记代码块,最终生成的代码将 尝试在代码执行时获取锁,如果已经获取锁,则正在执行的代码将 一直等待,直到锁可用为止。当代码退出同步代码块时,锁被释放。 还可以用效果等价于Monitor或一个用于Monitor代码块的编译器关 键字的MethodImplAttribute修饰方法并传递 MethodImplOptions.Synchronized。Thread.Interrupt可用于使线程跳 出阻塞操作(如等待访问同步代码区域),还用于使线程跳出 Thread.Sleep等操作