C#程序设计基础-教程、实验、习题[赵敏][电子教案]第十章

上传人:xins****2008 文档编号:232109138 上传时间:2023-09-13 格式:PPT 页数:39 大小:130.50KB
收藏 版权申诉 举报 下载
C#程序设计基础-教程、实验、习题[赵敏][电子教案]第十章_第1页
第1页 / 共39页
C#程序设计基础-教程、实验、习题[赵敏][电子教案]第十章_第2页
第2页 / 共39页
C#程序设计基础-教程、实验、习题[赵敏][电子教案]第十章_第3页
第3页 / 共39页
资源描述:

《C#程序设计基础-教程、实验、习题[赵敏][电子教案]第十章》由会员分享,可在线阅读,更多相关《C#程序设计基础-教程、实验、习题[赵敏][电子教案]第十章(39页珍藏版)》请在装配图网上搜索。

1、第10章C#多线程技术10.1线程概述在介绍线程概念之前,需要对进程与线程之间的关系做个说明。当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源。线程是比进程还细的单位。一个进程可以同时存在一个以上的线程,可以将其视为进程所执行的工作。例如,在一个线程完成计算任务的同时,另外一个线程可以对图像进行更新,两个线程可以同时处理同一个进程,并发出各自不同的网络请求。从概念上讲,线程提供了一种在一个软件中并行执行代码的方式每个线程都“同时”在一个共享的内存空间中执行指令。在具有一个处理器的计算机上,多个线程可以通过利用用户事件之间很小的时间段在后台处理数据来达到这

2、种效果。而在Client/Server模式下,服务器要不断监听来自多个客户端的请求,这时如果采用单线程机制的话,这个线程由于不断地循环监听客户端的请求,从而无法处理其他的任务。要让服务器同时为多个客户服务,则必须引入多线程技术。多线程通常应用在以下几种情况:(1)耗时的计算工作:如遇到复杂的数学计算时,需考虑创建新的线程对象,让应用程序在进行计算的同时,使其他的线程也能够进行指定的工作;(2)等待响应信息:当用户通过网络下载大量数据时,需要较长时间的等待响应,这时可考虑应用多线程技术,让处理器去执行其他的工作。10.2.NET对多线程的支持在.NET程序设计中,在System.Threadin

3、g命名空间下有一个Thread类,用于对线程进行管理。如创建线程、启动线程、终止线程、合并线程、让线程休眠等。(1)启动线程要启动某个线程,必须先创建该线程。创建线程的常用形式为:Threadt1=newThread(方法名);该线程通过委托执行指定的方法,如果方法中有参数,可在启动线程时传递实参。启动线程调用Start方法:t1.Start();/无参的方法t1.Start(“hello”);/有参方法(2)终止线程终止线程有两种方法:事先设置一个布尔类型字段,在其他线程中通过修改该布尔值表示是否需要终止该线程,在该线程中循环判断该布尔值,以确定是否退出线程。调用Thread类的Abort方

4、法,强行终止线程。t1.Abort();(3)暂停线程在多线程应用程序中,不希望某个线程继续执行,而希望暂停一段时间,可以调用Thread类的Sleep方法。Threads.Sleep(1000);/Sleep方法的参数是暂停时间的毫秒数(4)合并线程Join方法用于把指定的线程合并到当前线程中。如果一个线程t1在执行过程中需要等待另一个线程t2结束后才能继续执行,可以在t1的代码块中调用Join方法。t2.Join();t1在执行完此语句后会处于暂停状态,直到t2结束后才会继续执行。10.3一个多线程程序在C#应用程序中,第一个线程总是Main()方法,因为第一个线程是由.NET运行库开始执

5、行的,Main()方法是.NET运行库开始执行的第一个方法,后续的线程由应用程序在内部启动。线程的主要属性:CurrentThread:获取当前正在运行的线程。Name:设置或获取线程的名字。Priority:设置或获取线程的优先级。ThreadState:设置或获取线程的当前状态。IsBackground:指示线程是否为后台线程。IsAlive:指示当前线程的执行状态。【例10-1】打开VisualStudio.NET,新建一个控制台应用程序(ConsoleApplication),编辑以下代码:usingSystem;namespaceex1003usingSystem;usingSyst

6、em.Threading;namespaceThreadTestpublicclassAlphapublicvoidBeta()while(true)Console.WriteLine(Alpha.Betaisrunninginitsownthread.);publicclassSimplepublicstaticintMain()Console.WriteLine(ThreadStart/Stop/JoinSample);AlphaoAlpha=newAlpha();ThreadoThread=newThread(newThreadStart(oAlpha.Beta);oThread.Sta

7、rt();while(!oThread.IsAlive);Thread.Sleep(1);oThread.Abort();oThread.Join();Console.WriteLine();Console.WriteLine(Alpha.Betahasfinished);tryConsole.WriteLine(TrytorestarttheAlpha.Betathread);oThread.Start();catch(ThreadStateException)Console.Write(ThreadStateExceptiontryingtorestartAlpha.Beta.);Cons

8、ole.WriteLine(Expectedsinceabortedthreadscannotberestarted.);Console.ReadLine();return0;这段程序包含两个类Alpha和Simple,在创建线程oThread时用指向Alpha.Beta()方法初始化ThreadStart代理(delegate)对象。当创建的线程oThread调用oThread.Start()方法启动时,实际上程序运行的是Alpha.Beta()方法:AlphaoAlpha=newAlpha();ThreadoThread=newThread(newThreadStart(oAlpha.Be

9、ta);oThread.Start();然后在Main()函数的while循环中,使用静态方法Thread.Sleep()让主线程停了1ms,这段时间CPU转向执行线程oThread。然后试图用Thread.Abort()方法终止线程oThread,注意后面的oThread.Join(),Thread.Join()方法使主线程等待,直到oThread线程结束。可以给Thread.Join()方法指定一个int型的参数作为等待的最长时间。之后,试图用Thread.Start()方法重新启动线程oThread,但是Abort()方法带来的后果是不可恢复的终止线程,所以程序最后会抛出ThreadSt

10、ateException异常。程序执行结果如图10.1所示,此例中Main()函数是C#程序的入口,起始线程可以称为主线程,如果所有的前台线程都停止了,那么主线程可以终止,而所有的后台线程都将无条件终止。所有的线程虽然在微观上是串行执行的,但在宏观上完全可以认为它们在并行执行。10.4线程的优先级当线程之间争夺CPU时,CPU是按照线程的优先级给予服务的。在C#应用程序中,用户可以设定5个不同的优先级,由高到低分别是:Highest、AboveNormal、Normal、BelowNormal、Lowest。在创建线程时如果不指定优先级,那么系统默认为ThreadPriority.Normal

11、。给一个线程指定优先级,可以使用如下代码:myThread.Priority=ThreadPriority.Lowest;/设定优先级为最低通过设定线程的优先级,可以安排一些相对重要的线程优先执行,如对用户的响应等。以下两个例子演示设置不同线程优先级别产生的不同效果。【例10-2】两个线程优先级相同(均为默认值Normal),所以它们交替进行,每次运行,这两个线程被执行的几率大致相等。运行结果如图10.2所示。usingSystem;usingSystem.Threading;namespaceex10_1classProgramstaticvoidMain(stringargs)Thread

12、ThreadA=newThread(delegate()for(inti=0;i=10000000;i+)if(i%1000000=0)Console.Write(A););ThreadThreadB=newThread(delegate()for(inti=0;i=10000000;i+)if(i%1000000=0)Console.Write(B););ThreadA.Start();ThreadB.Start();Console.ReadLine();【例10-3】通过设置两个线程的优先级,查看运行结果。运行结果如图10.3所示。usingSystem;usingSystem.Threa

13、ding;namespaceex10_2classProgramstaticvoidMain(stringargs)ThreadThreadA=newThread(delegate()for(inti=0;i=20000000;i+)if(i%1000000=0)Console.Write(A););ThreadThreadB=newThread(delegate()for(inti=0;i=20000000;i+)if(i%1000000=0)Console.Write(B););ThreadA.Priority=ThreadPriority.AboveNormal;ThreadB.Prio

14、rity=ThreadPriority.BelowNormal;ThreadA.Start();ThreadB.Start();Console.ReadLine();系统优先执行优先级较高的线程,但这只意味着优先级较高的线程占有更多的CPU时间,并不意味着一定要先执行完优先级较高的线程,才会执行优先级较低的线程。这点从运行结果中也可以看出,线程B偶尔会出现在主线程和线程A前面。若将线程A和线程B的优先级别对换,即改写下面程序段:ThreadA.Priority=ThreadPriority.BelowNormal;ThreadB.Priority=ThreadPriority.AboveNor

15、mal;10.5线程同步在应用程序中使用多线程的一个好处是,每个线程都可以异步执行。对于Windows应用程序,耗时的任务可以在后台执行,而使应用程序窗口和控件保持响应。对于服务器应用程序,多线程处理提供了用不同线程处理每个传入请求的能力。否则,在完全满足前一个请求之前,将无法处理新请求。然而,线程的异步特性意味着必须协调对资源(如文件句柄、网络连接和内存)的访问。否则,两个或更多的线程可能在同一时间访问相同的资源,而每个线程都不知道其他线程的操作。结果将产生不可预知的数据损坏。为了保护应用程序的资源不被破坏,为多线程程序提供了三种加锁的机制,分别是Monitor类、lock关键字和Mutex

16、类。通过对引用的对象申请一个“锁”,一旦一段程序获得该“锁”的控制权后,就可以保证只有它才能够对该对象进行操作。同样,利用这种锁,一个线程可以一直处于等待状态,直到有能够唤醒它的信号(通过变量传来)为止。1locklock实现的功能是:使后进入的线程不会中断当前的线程,而是等待当前线程结束后再继续执行。应用:privateObjectthisLock=newobject();lock(x)/锁定的代码块,通常为使用x的语句避免锁定public类型,否则实例将超出代码的控制范围。最佳做法是定义private对象来锁定,或privatestatic对象变量来保护所有实例所共有的数据。【例10-4】

17、使用lock示例。运行结果如图10.5所示,代码如下:usingSystem;usingSystem.Threading;namespaceConsoleApplication5classProgramstaticvoidMain(stringargs)Console.WriteLine(程序开始时间:+DateTime.Now.ToString();Exampleex=newExample();Threadthreads=newThread5;for(inti=0;i0,Thread.CurrentThread.Name);Thread.Sleep(1000);Console.WriteLi

18、ne(DateTime.Now);如果把程序中的锁解除,即如下程序,程序执行混乱,可能有多种结果出现usingSystem;usingSystem.Threading;namespaceConsoleApplication5classProgramstaticvoidMain(stringargs)Console.WriteLine(程序开始时间:+DateTime.Now.ToString();Exampleex=newExample();Threadthreads=newThread5;for(inti=0;i0,Thread.CurrentThread.Name);Thread.Slee

19、p(1000);Console.WriteLine(DateTime.Now);2Monitorlock是对Monitor(监视器)的Enter和Exit的一个封装,因此Monitor类的Enter()和Exit()方法的组合使用可以用lock关键字替代。Monitor类除了具有lock的功能外,还有以下功能:TryEnter()解决长期死锁的问题,如果并发经常发生,且持续时间很长,使用TryEnter可以有效防止死锁或长时间的等待。Wait()释放对象上的锁,以便允许其他线程锁定和访问该对象。在其他线程访问对象时,调用线程将等待。Pulse()、PulseAll()向一个或多个等待线程发送信

20、号。该信号通知等待线程锁定对象的状态已更改,并且锁的所有者准备释放该锁。等待线程被放置在对象的就绪队列中,以便它可以最后接收对象锁。一旦线程拥有了锁,就可以检查对象的新状态以查看是否达到所需状态。【例10-5】用Monitor类来实现例10-4的同步处理。结果与例10-4加lock锁相同,代码如下:usingSystem;usingSystem.Threading;namespaceConsoleApplication5classProgramstaticvoidMain(stringargs)Console.WriteLine(程序开始时间:+DateTime.Now.ToString();

21、Exampleex=newExample();Threadthreads=newThread5;for(inti=0;i0,Thread.CurrentThread.Name);Thread.Sleep(1000);Console.WriteLine(DateTime.Now);Monitor.Exit(this);3Mutex互斥体Mutex与Monitor很相似,只有拥有互斥对象的线程才具有访问资源的权限。由于互斥对象只有一个,因此就决定了任何情况下此共享资源都不会同时被多个线程所访问。当前占据资源的线程在任务处理完后应将拥有的互斥对象交出,以便其他线程在获得后得以访问资源。互斥量比临界区

22、复杂,因为使用互斥不仅能够在同一应用程序不同线程中实现资源的安全共享,而且可以在不同应用程序的线程之间实现对资源的安全共享。.NET中互斥体由Mutex类来表示,线程使用Mutex.WaitOne()方法等待C#Mutex对象被释放,如果它等待的C#Mutex对象被释放了,就自动拥有这个对象,直到它调用Mutex.ReleaseMutex()方法释放这个对象,而在此期间,其他想要获取这个C#Mutex对象的线程都只能等待。【例10-6】使用Mutex同步代码块示例。运行界面如图10.7所示,代码如下:usingSystem;usingSystem.Threading;classProgramT

23、hreadthread1=null;Threadthread2=null;Mutexmutex=null;publicProgram()mutex=newMutex();thread1=newThread(newThreadStart(thread1Func);thread2=newThread(newThreadStart(thread2Func);publicvoidRunThread()thread1.Start();Thread.Sleep(1000);thread2.Start();privatevoidthread1Func()for(intcount=0;count10;coun

24、t+)lock(this)mutex.WaitOne();TestFunc(Thread1haverun+count.ToString()+times);mutex.ReleaseMutex();privatevoidthread2Func()for(intcount=0;count10;count+)lock(this)mutex.WaitOne();TestFunc(Thread2haverun+count.ToString()+times);mutex.ReleaseMutex();privatevoidTestFunc(stringstr)Console.WriteLine(01,st

25、r,System.DateTime.Now.Millisecond.ToString();Thread.Sleep(30);staticvoidMain(stringargs)Programp=newProgram();p.RunThread();Console.ReadLine();【例10-7】创建两个程序,它们每隔1s向文件test.txt中写入一条包含当前时间的记录。为了保证每条记录的完整性,当一个程序向文件写入记录时,另一个程序必须等待。需要使用互斥。运行界面如图10.8所示。创建第一个程序ex10_6threadA,在主函数中加如下代码:usingSystem.Threading;

26、usingSystem.IO;namespaceex10_6threadAclassProgramstaticvoidMain(stringargs)ThreadthreadA=newThread(delegate()/创建互斥体m,其中false表示创建者是否具有初始所有权Mutexm=newMutex(false,ttt);/“ttt”是互斥体的系统名称。操作系统根据互斥体的名称辨别互斥体,不管互斥体对象创建于哪个应用程序中stringfilename=E:zmc#exampletest.txt;for(inti=1;i10;i+)try/请求互斥体的所有权,若成功则进入临界区,否则等待m

27、.WaitOne();/在临界区中,操作临界资源,即向文件中写入数据File.AppendAllText(filename,ThreadA+DateTime.Now+rn);catch(System.Threading.ThreadInterruptedException)Console.WriteLine(线程A被中断);finallym.ReleaseMutex();/释放互斥体的所有权Thread.Sleep(1000););threadA.Start();创建第二个程序ex10_6threadB,在主函数中加如下代码:usingSystem.Threading;usingSystem.

28、IO;namespaceex10_6threadBclassProgramstaticvoidMain(stringargs)ThreadthreadB=newThread(delegate()Mutexm=newMutex(false,ttt);stringfilename=E:zmc#exampletest.txt;for(inti=1;i10;i+)trym.WaitOne();File.AppendAllText(filename,ThreadB+DateTime.Now+rn);catch(System.Threading.ThreadInterruptedException)Con

29、sole.WriteLine(线程B被中断);finallym.ReleaseMutex();Thread.Sleep(1000););threadB.Start();/启动ex10_6threadA.exe程序System.Diagnostics.Process.Start(ex10-6threadA.exe);上面两个应用程序,分别创建了一个互斥体对象,因为它们的系统名都是“ttt”,把两个应用程序的可执行文件复制到一个文件夹中,启动ex10_6threadB.exe文件,也会启动ex10_6threadA.exe文件。因为ex10_6threadB.exe文件中包含启动ex10_6threadA.exe的代码。结果显示,两个程序交替向文件test.txt中写入记录,当一个应用程序写入时,另一个应用程序必须等待。

展开阅读全文
温馨提示:
1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
2: 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
3.本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
关于我们 - 网站声明 - 网站地图 - 资源地图 - 友情链接 - 网站客服 - 联系我们

copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!