资源描述
,*,版权所有,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,单击此处编辑母版标题样式,C#,程序设计与案例教程 清华大学出版社,第七章 线程,线程是程序运行的基本单元。多线程编程就是将程序任务分成几个并行的子任务,各个子任务相对独立地并发执行,这样可以提高程序的性能和效率。,C#,语言提供了内置的多线程机制。,1,版权所有,目录,线程及其创建,线程的基本控制,线程同步,线程通信,线程池,2,版权所有,7.1,线程及其创建,线程与进程相似,是一段完成某个特定功能的代码,是程序中的一个执行流;但与进程不同的是,同一个类的多个线程是共享一块内存空间和一组系统资源,而线程本身的数据通常只有微处理器的寄存器数据,以及一个供程序执行时使用的堆栈。所以系统在产生一个线程,或者在各个线程之间切换时,负担要比进程小的多,正因如此,线程被称为轻量级进程。,3,版权所有,7.1.1,线程与进程的比较,(,1,)基于进程的特点是允许计算机同时运行两个或更多的程序。,(,2,)基于线程的多任务处理环境中,线程是最小的处理单位。,(,3,)多个进程的内部数据和状态都是完全独立的,而多线程是共享一块内存空间和一组系 统资源,有可能互相影响。,(,4,)线程本身的数据通常只有寄存器数据,以及一个程序执行时使用的堆栈,所以线程的切换比进程切换的负担要小。,4,版权所有,7.1.2,线程的优点,有多个线程的进程能够用线程管理互斥的任务,比如提供用户一个界面和完成后台计算。创建一个多线程的进程能够方便地让程序并发执行几个类似的或相同的任务。,例如,单个应用程序域可以使用多个线程来完成以下任务:,(,1,)通过网络与,Web,服务器和数据库进行通信。,(,2,)执行占用大量时间的操作。,(,3,)区分具有不同优先级的任务。例如,高优先级线程管理时间关键的任务,低优先级线程执行其他任务。,(,4,)使用户界面可以在将时间分配给后台任务时仍能快速作出响应。,5,版权所有,7.1.3,建立线程,创建线程的基本步骤如下:,(,1,)声明该线程。,System.Threading.Thread,myThread,;,(,2,)用线程起始点的适当委托实例化该线程。在,C#,中创建新的,ThreadStart,对象。,myThread,=new,System.Threading.Thread(new,System.Threading.ThreadSt,art(myStartingMethod,);,(,3,)调用,Thread.Start,(),方法启动线程。,myThread.Start,();,6,版权所有,【,案例,7-1,】,随机画圆。,本案例是一个随机画圆的程序。每次画的位置、大小、颜色都是随机的。,7,版权所有,7.2,线程的基本控制,线程从创建到灭亡主要经历五种状态,借助,Thread,类所提供的方法,我们可以实现这些状态之间的转换,从而达到对线程的控制。通过本节内容重点掌握线程的基本控制方法、线程的优先级及其他有关概念。,8,版权所有,7.2.1,线程的状态,一个线程从被创建到停止执行要经历一个完整的生命周期。在这个生命周期中线程处于不同的状态。线程的状态用来表明线程的活动情况及线程在当前状态中能够完成的功能。线程的生命周期有五种状态。,9,版权所有,7.2.2 Thread,类和线程控制,1,Thread,主要的属性,(,1,),CurrentThread,:,获取当前正在运行的线程。,(,2,),IsAlive,:,获取一个值,,,该值指示当前线程的执行状态。,(,3,),IsBackground,:获取或设置一个值,该值指示某个线程是否为后台线程。,(,4,),IsThreadPoolThread,:获取一个值,该值指示线程是否属于托管线程池。,(,5,),Name,:获取或设置线程的名称。,(,6,),Priority,:获取或设置一个值,该值指示线程的调度优先级。,10,版权所有,2,Thread,的主要方法,(,1,),Abort(),:,在调用此方法的线程上引发,ThreadAbortException,,,以开始终止此线程的过程。调用此方法通常会终止线程。,(,2,),Interrupt(),:中断处于,WaitSleepJoin,线程状态的线程。,(,3,),Join(),:阻塞调用线程,直到某个线程终止时为止。,(,4,),ResetAbort,(),:取消为当前线程请求的,Abort,。,(,5,),Resume(),:继续已挂起的线程。,(,6,),Sleep(int,),:将当前线程阻塞指定的毫秒数。,(,7,),Start(),:导致操作系统将当前实例的状态更改为,ThreadState.Running,。,(,8,),Suspend(),:挂起线程,或者如果线程已挂起,则不起作用。,(,9,),ThreadState,(),:获取一个值,该值包含当前线程的状态。,11,版权所有,【,案例,7-2,】,图像浏览程序。,本案例是一个图像浏览程序,能够自动切换图片,并可以通过“开始”、“停止”、“暂停”、“恢复”、“下页”、“上页”等按钮控制图像的浏览。,12,版权所有,7.3,线程同步,在包含多个线程的应用程序中,线程间有时会共享存储空间。当两个或多个线程同时访问同一共享资源时,必然会出现冲突问题。如,一个线程可能尝试从一个文件中读取数据,而另一个线程则尝试在同一文件中修改数据。在这种情况下,数据可能会变的不一致。我们需要做的是允许一个线程彻底完成其任务后,再允许下一个线程执行。必须保证一个共享资源一次只被一个线程使用。实现此目的的过程称为,同步,。,13,版权所有,7.3.1,使用,lock(),语句,多个线程共享资源时,,C#,提供了,lock,,它可以把一段代码定义为互斥段,互斥段在一个时刻内只允许一个线程进入执行,而其他线程必须等待。基本的格式为:,lock,(,对象或类,),语句块,14,版权所有,【,案例,7-3,】,模拟取款程序。,本案例模拟取款程序,有一笔存款,三个人同时在取钱,每个人可取,5,次,每次当取款时,若数额超过现有的存款额,那么取款被拒绝。要保证一个人正在取款时,另一个人不能取。存款的数额永远不为负。,15,版权所有,7.3.2 Monitor,类,在实现线程同步时,可以使用,Monitor,类。该类通过向单个线程授予对象锁来控制对对象的访问。对象锁提供限制访问代码块(通常称为临界区)的能力。当一个线程拥有对象的锁时,其他任何线程都不能获取该锁。,Monitor,具有以下功能,:,(,1,)它根据需要与某个对象相关联。,(,2,)它是未绑定的,也就是说可以直接从任何上下文调用它。,(,3,)不能创建,Monitor,类的实例。,(,4,)将为每个同步对象来维护以下信息:,对当前持有锁的线程的引用。,对就绪队列的引用,它包含准备获取锁的线程。,对等待队列的引用,它包含正在等待锁定对象状态变化通知的线程。,16,版权所有,常用的方法有:,(,1,),Enter(),TryEnter,(),:获取对象锁。此操作同样会标记临界区的开头。其他任何线程都不能进入临界区,除非它使用其他锁定对象执行临界区中的指令。,(,2,),Wait(),:释放对象上的锁以便允许其他线程锁定和访问该对象。在其他线程访问对象时,调用线程将等待。脉冲信号用于通知等待线程有关对象状态的更改。,(,3,),Pulse(),,,PulseAll,(),:向一个或多个等待线程发送信号。该信号通知等待线程锁定对象的状态已更改,并且锁的所有者准备释放该锁。等待线程被放置在对象的就绪队列中以便它可以最后接收对象锁。一旦线程拥有了锁,它就可以检查对象的新状态以查看是否达到所需状态。,(,4,),Exit(),:释放对象上的锁。此操作还标记受锁定对象保护的临界区的结尾。,17,版权所有,【,案例,7-4,】,模拟调动程序。,本案例是个模拟调度的程序。对给定数量的车皮进行调度,图(,a,)为没有使用同步控制的运行结果,图(,b,)为使用同步控制的运行结果。,18,版权所有,7.3.3 Interlocked,类,此类为多个线程共享的变量提供原子操作。主要有如下方法:,(,1,),CompareExchange,(),:比较两个值是否相等,如果相等,则替换其中一个值。,(,2,),Decrement(),:以原子操作的形式递减指定变量的值并存储结果。,(,3,),Exchange(),:以原子操作的形式将变量设置为指定的值。,(,4,),Increment(),:以原子操作的形式递增指定变量的值并存储结果。,19,版权所有,7.3.4,Mutex,类,Mutex,类用于线程或线程间的同步原语操作。当两个或更多线程需要同时访问一个共享资源时,系统需要使用同步机制来确保一次只有一个线程使用该资源。,Mutex,是同步原语,它只向一个线程授予对共享资源的独占访问权。如果一个线程获取了互斥体,则要获取该互斥体的第二个线程将被挂起,直到第一个线程释放该互斥体。,20,版权所有,1,Mutex,的构造函数,(,1,),public,Mutex,(),:使用默认属性初始化,Mutex,类的新实例。,(,2,),public,Mutex(bool,),:使用指示调用线程是否应拥有互斥体的初始所属权的布尔值来初始化,Mutex,类的新实例。,(,3,),public,Mutex(bool,string),:使用指示调用线程是否应拥有互斥体的初始所属权的布尔值和作为互斥体名称的字符串来初始化,Mutex,类的新实例。,(,4,),public,Mutex(bool,string,bool,),:使用指示调用线程是否应拥有互斥体的初始所属权的布尔值、作为互斥体名称的字符串以及在方法返回时指示调用线程是否已被授予互斥体的初始所属权的布尔值来初始化,Mutex,类的新实例。,21,版权所有,2,Mutex,的主要方法,(,1,),Close(),:在派生类中被重写时,释放由当前,WaitHandle,持有的所有资源。,(,2,),ReleaseMutex,(),:释放,Mutex,一次。,(,3,),WaitOne,(),:阻塞当前线程,直到当前的,WaitHandle,收到信号。,22,版权所有,7.4,线程通信,7.4.1,使用,Monitor,实现线程通信,Lock,语句与,Monitor,类配合,不仅可以实现线程同步,而且借助,Monitor,类的,Wait(),、,Pulse,()、,PulseAll,(),方法还可以进行线程通信。,23,版权所有,【,案例,6-5,】,吃苹果。,本案例是一个模拟吃苹果的程序。爸爸、妈妈不断往盘子里放苹果,三个孩子:老大、老二和老三不断地从盘子里取苹果吃。五个线程需要同步执行,并且要互相协调。爸爸和妈妈放苹果时,盘子里要有地方,而且两个人不能同时放。三个孩子取苹果时,盘子里要有苹果,而且也不能同时取。此外,三个孩子因吃苹果速度不同,取苹果的频率不一样,又因大小不同,有不同的优先级。,24,版权所有,7.4.2,使用,AutoResetEvent,类和,ManualResetEvent,类进行线程通信,AutoResetEvent,允许线程通过发信号互相通信。,ManualResetEvent,类与,AutoResetEvent,的使用基本类似。所不同的是,,AutoResetEvent,在单个等待线程被释放后由系统自动重置为无信号的状态。,25,版权所有,【,实例,7-6,】,哲学家用餐。,哲学家用餐问题是典型的线程序间通信的问题。五位哲学家坐在餐桌前,他们在思考并在感到饥饿时就吃东西。每两位哲学家之间只有一根筷子,为了吃东西,一位哲学家必须要用两根筷子。如果每位哲学家拿起右筷子,然后等着拿左筷子,问题就产生了。在这种情况下,就会发生死锁。当哲学家放下筷子时,要通知其他等待拿筷子的哲学家。,26,版权所有,7.5,线
展开阅读全文