资源描述
,单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,*,C#多线程-,主要内容,1,多线程概述,2thread,类,3,线程的生命周期,4,线程的优先级,5,线程的同步,6,多线程的自动管理,6.1,线程概述,进程,:是应用程序的一个运行例程,是应用程序的一次动态执行过程。,线程,:是进程中的一个执行单元;是操作系统分配,CPU,时间的基本单元。,Windows,是一个支持多线程的系统。进程相当于一个容器,一个进程可以包含若干个线程。,多线程的概念,多线程:在同一时间执行多个任务的功能,称为多线程或自由线程。,多线程的优点:提高,CPU利用率。,主要缺点:,对资源的共享访问可能造成冲突(,对共享资源的访问进行同步或控制,);程序的整体运行速度减慢等等。,但从微观上讲,对单,CPU线程还是串行的。,多线程,程序,1,线程,1,线程,2,线程,3,单独的执行路径,多线程,在以下情况中可能要使用到多线程:,程序需要同时执行两个或多个任务,程序要等待某事件的发生,例如用户输入、文件操作、网络操作、搜索等,后台程序,任何程序在执行时,至少有一个主线程。主线程创建其他的附加线程。,第一个线程总是,Main(),方法,因为第一个线程是由,.NET,运行库开始执行的,,Main(),方法是,.NET,运行库选择的第一个方法。,主线程,6.2 .NET,对多线程的支持,在,.NET,程序设计中,线程是使用,Thread,类,(或,Timer,类(线程计数器)、,ThreadPool,类(线程池),来处理的,这些类在,System.Threading,命名空间中:,Thread,类:,(实现线程的主要方法),一个,Thread,实例管理一个线程,即执行序列。通过简单实例化一个,Thread,对象,就可以创建一个线程,然后通过,Thread,对象提供的方法对线程进行管理。,Timer,类:适用于间隔性的完成任务。,ThreadPool,:适用于多个小的线程。,Thread,类的主要属性,1,、,CurrentThread,:获取当前正在运行的线程。,2,、,Name,:获取或设置线程的名称。,3,、,Priority,:获取或设置线程的优先级。,4,、,TreadState,:获取或设置线程的当前状态。,5,、,IsBackground,:指示线程是否为后台线程。,6,、,IsAlive,:指示当前线程的执行状态。,7,、,CurrentContext,:获取线程其中执行的当前上下文。,Thread,类的主要方法,1,、,Start,:启动线程。,2,、,Suspend,;挂起线程。,3,、,Resume,:继续已挂起的线程。,4,、,Interrupt,:中断处于,WaitSleepJoin,线程状态的线程。,5,、,Join,:,阻塞调用线程,直到某个线程终止时为止。,6,、,Sleep,:将当前线程阻塞指定的毫秒数。(休眠),7,、,Abort,:终止线程。,8,、,ResetAbort,:取消为当前线程请求的,Abort,。,线程的建立与启动,新建一个线程的过程:,只需将其声明并为其提供线程起始点处的方法委托,再用,Thread.Start(),方法启动该线程,(1),Thread,a=new Thread(new ThreadStart(b);,其中,,b,为新建过程中执行的过程名。,(2)调用,Thread.Start(),方法启动该线程,a.Start();,常用委托(了解),在System.Threading中,ThreadStart、ParameterizedThreadStart是最常用到的委托。,在C#中,线程入口是通过ThreadStart代理(delegate)来提供的,ThreadStart相当于一个函数指针,指向线程要执行的函数,当调用Thread.Start()方法后,线程就开始执行ThreadStart所代表或者说指向的函数。,由ThreadStart生成的线程是最直接的方式。,ParameterizedThreadStart是为异步触发带参数的方法而设的。,线程的休眠、挂起、恢复与终止,线程的休眠和挂起,(,1,)调用,Thread.Sleep(),方法将当前线程休眠指定的时间。,注:,Sleep(),方法指定的时间以毫秒为单位。,(,2,)调用,s1.Suspend(),方法将线程挂起,区别:前者为静态方法,并且使线程立即暂停一定时间;后者为实例方法,不会使线程立即停止执行,直到线程到达安全点之后,它才将该线程暂停。,线程的恢复与终止,调用,Resume(),方法将线程恢复;,调用,Abort(),方法将线程终止;,Join、Interrupt,Join(),:使一个线程等待另一个线程停止(让渡优先权),当前线程调用别的线程Join时,当前线程就会进入等待状态,等待调用线程完成所有操作后,当前线程才能继续执行(假设在A线程中调用了B.Join()那么就好像A对B说“B同志你赶快做,等你做完了我再继续”)。,Interrupt(),:中断处于,Join,Wait,(?有木有),Sleep,线程状态的线程。(,?如何继续执行,),终止线程(Abort),若想终止正在运行的线程,可以使用Abort()方法。在使用Abort()的时候,将引发一个特殊异常 ThreadAbortException。,若想在线程终止前恢复线程的执行,可以在捕获异常后,在catch(ThreadAbortException ex).中调用Thread.ResetAbort()取消终止。,线程的生命周期,线程的生命周期,前台线程和后台线程,Thread.Start()启动的线程默认为前台线程,应用程序域必须等待所有前台线程运行结束后,才会自动卸载。,后台线程:,将线程,t,hread的IsBackground属性,设置为true,把线程设置为后台线程!这时应用程序域将在主线程完成时就被卸载,而不会等待异步线程的运行。,6.4,线程的优先级,当线程之间争夺CPU时间时,CPU按照是线程的优先级给予服务的。高优先级的线程可以完全阻止低优先级的线程执行,因此在改变线程的优先级时要小心。,每个线程在创建时其优先级为,:,ThreadPriority.Normal,线程的优先级定义为,ThreadPriority,枚举类型,如下表:,6.5,线程的同步(异步),同步:是指在某一时刻只有一个线程可以访问某共享数据,.,异步:多个线程同时访问某个共享数据,会造成数据混乱。,背景:,当多个线程共享数据,其中一个或多个线程要修改数据时,有可能引起数据不统一等问题。,2,、在,C#,中处理同步,通过对指定对象的加锁和解锁可以实现同步代码段的访问。,在,.NET,的,System.Threading,命名空间中提供了,Monitor,类来实现加锁与解锁。该类中的方法都是静态的。如下表:,Monitor(监视器),Monitor.Enter(object)方法是获取锁,Monitor.Exit(object)方法是释放锁,为了避免在获取锁后发生异常,导致锁无法释放,所以要在finally块中释放锁。,try,Monitor.Enter(object);,/do something,finally,Monitor.Exit(object);,C#,中,lock,关键字提供了与,Monitoy.Enter,和,Monitoy.Exit,同样的功能,这种方法用在你的代码段不能被其他独立的线程中断的情况。通过对,Monitor,类的简易封装,,lock,为同步访问变量提供了一个非常简单的方式,其用法如下:,lock(this),/,要使用的语句,lock,语句把变量放在圆括号中,以包装对象,称为独占锁或排它锁。当执行带有,lock,关键字的复合语句时,独占锁会保留下来。当变量被包装在独占锁中时,其他线程就不能访问该变量。如果在上面的代码中使用独占锁,在执行复合语句时,这个线程就会失去其时间片。如果下一个获得时间片的线程试图访问变量,就会被拒绝。,Windows,会让其他线程处于睡眠状态,直到解除了独占锁为止。,lock,与,Monitor,区别,lock,锁定一段代码,,Monitor,锁定一个对象。,Monitor,类可以锁定一个对象,一个线程只有得到这把锁才可以对该对象进行操作。对象锁机制保证了,在可能引起混乱的情况下一个时刻只有一个线程可以访问这个对象。,3,、同步时要注意的问题,线程同步会降低性能。原因有两个:,一、在对象上放置和解开锁会带来某些系统开销(较小),二、线程同步使用得越多,等待释放对象的线程就越多。如果一个线程在对象上放置了一个锁,需要访问该对象的其他线程就只能暂停执行,直到该锁被解开,才能继续执行。,lock,语句在某种意义上就是临时禁用应用程序的多线程功能,也就临时删除了多线程的各种优势。,对于线程,系统在创建时不仅要给其分配资源,而且还要在线程之间互相切换、销毁,这些都会造成程序性能降低。,线程池并不会在,CLR初始化的时候立刻建立线程,而是在应用程序要创建线程来执行任务时,线程池才初始化一个线程。在完成任务以后,该线程不会自行销毁,而是以挂起的状态返回到线程池。直到应用程序再次向线程池发出请求时,线程池里挂起的线程就会再度激活执行任务。这样既节省了建立线程所造成的性能损耗,也可以让多个任务反复重用同一线程,从而在应用程序生存期内节约大量开销。,多线程的自动管理:,ThreadPool,(线程池),6.6,多线程的自动管理,下列情况下考虑线程池:,一:应用程序中,线程把大部分的时间花费在等待状态,等待某个事件发生,然后才能给予响应,这一般使用,ThreadPool,(线程池)来解决;,二:线程平时都处于休眠状态,只是周期性地被唤醒这一般使用,Timer,(定时器)来解决,;,线程池中的线程默认为后台线程,即它们的IsBackground 属性为 true。这意味着在所有的前台线程都已退出后,线程池线程会自动关闭。线程池通过线程命名空间的ThreadPool类来实现,要请求由线程池中的一个线程来处理你的任务,需要调用QueueUserWorkItem方,法。,代码:,ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadInvoke);,多线程的自动管理:,ThreadPool,(线程池),Timer,类:,设置一个定时器,定时执行用户指定的函数。定时器启动后,系统将自动建立一个新的线程,执行用户指定的函数。,初始化:,Timer timer=new Timer(timerDelegate,s,1000,1000);,多线程的自动管理,(,定时器,Timer),6.7,应用实例,(,两个关于线程或进程的例子,),综合例题,1,:通过,Process,类获取系统进程列表。运行界面如下图所示:,综合例题2跨线程操作,在,C#,中,经常用到这样一个场景,,Windows Form,程序启动一个工作者线程执行一部分工作,这样做是为了避免速度慢的工作如果直接调用会使得主,Form,停止响应一段时间。工作线程处理中可能想操作某个主线程的,Windows Form,的,Control,,比如按钮,,ListView,等等更新工作状态之类,直接控制是不行的,不能够跨线程操作另一个线程创建的,Windows Form,控件。要使用委托去调用,。,
展开阅读全文