1、10-1第 10 章 C#的高级应用本章介绍 C#程序的高级应用,包括多线程编程、 ADO.NET 数据库编程、ASP.NET及 Web 编程、XML 及 Web Service 编程、网络通讯编程、 C#程序与其他程序的互操作等。10.1 多线程编程以前所介绍的程序多数是单线程的,即一个程序只有一条从头至尾的执行路线。然而现实世界中的很多过程都具有多种途径同时运作,例如服务器可能需要同时处理多个客户机的请求。在 Windows 操作系统中,不仅多个应用程序能同时运行,而且在同一程序内部也可以有多个线程同时运行,这就是多线程。C#语言可以方便地开发出具有多线程功能的应用程序。10.1.1 多线
2、程的相关概念1程序、进程与线程程序是一段静态的代码,它是应用软件执行的蓝本。进程是应用程序的一次动态执行过程,它对应了从代码加载、执行到执行完毕的一个完整过程,这个过程也是进程本身从产生、发展到消亡的过程。作为执行蓝本的同一段程序,可以被多次加载到系统的不同内存区域分别执行,形成不同的进程。线程是比进程更小的执行单位。一个进程在其执行过程中,可以产生多个线程,形成多条执行线索。每条线索有它自身的产生、存在和消亡的过程,也是一个动态的概念。每个进程都有一段专用的内存区域,而线程间可以共享相同的内存单元(包括代码与数据) ,并利用这些共享单元来实现数据交换、实时通信与必要的同步操作。2多线程处理的
3、优点多线程处理可以同时运行多个任务。例如,文字处理器应用程序在处理文档的同时,可以检查拼写(作为单独的任务) ,或者完成打印的任务。由于多线程应用程序将程序划分成独立的任务,因此可以在以下方面显著提高性能:(1)多线程技术使程序的响应速度更快,因为用户界面可以在进行其他工作的同时一直处于活动状态。(2)当前没有进行处理的任务可以将处理器时间让给其他任务。(3)占用大量处理时间的任务可以定期将处理器时间让给其他任务。(4)可以随时停止任务。(5)可以分别设置各个任务的优先级以优化性能。一般地说,耗时、大量占用处理器并阻塞用户界面操作的任务,或者各个任务必须等待外部资源(如远程文件或 Intern
4、et 连接)等任务最适合用多线程来处理。3Thread 类线程要用到 System.Threading 名字空间,其中 System.Threading.Thread 类用于表示线程。Thread 类最常用的一些属性和方法列举在 表 10-1 和 表 10-2 中。10-2表 10-1 Thread 类的常用属性Property 描 述 CurrentPrincipal 获取或者设定线程的当前安全性 CurrentThread 获得对当前正在运行的线程的一个引用(static 属性) IsAlive 如果线程已经被启动并且尚在生命周期内,则返回 TrueIsBackground 如果目标线程是
5、在后台执行的,则为此属性赋值为 TrueName 获取或者设定这个线程的名字 Priority 获取或者设定这个线程的优先级 ThreadState 获得线程的当前状态 表 10-2 Thread 类的常用方法Method 描 述 Abort 撤消这个线程 Interrupt 如果线程处于 WaitSleepJoin 状态,则中断它 Join 等待一个线程的结束 Resume 将被挂起的线程重新开始 Sleep 让线程休眠一定时间 Start 启动一个线程 Suspend 挂起一个线程 10.1.2 线程的创建与控制1线程的创建Thread 类是一个 sealed 类,不能被继承,但可以创建
6、Thread 类的实例。Thread 类有一个构造方法,格式如下:public Thread( ThreadStart fun );其中 ThreadStart 是一个指代:public delegate void ThreadStart();线程所指代的方法,称为线程方法或线程函数。线程方法不带参数,且返回 void 类型。如果要传递相关的信息,则可以使用对象的成员变量或方法。下面是创建一个 Thread 对象并启动这个线程的一般方法:Thread thread = new Thread( new ThreadStart( obj.fun );thread.Start();例 10-1 Th
7、readTest.cs 创建多个线程。1 using System;2 using System.Threading;3 class Test4 5 static void Main()6 7 Test obj1 = new Test();8 Thread thread1 = new Thread( new ThreadStart( obj1.Count );9 thread1.Name = “线程 1“;10-31011 Test obj2 = new Test();12 Thread thread2 = new Thread( new ThreadStart( obj2.Count );1
8、3 thread2.Name = “线程 2“;1415 Thread thread3 = new Thread( new ThreadStart( obj2.Count );16 thread3.Name = “线程 3“;1718 thread1.Start();19 thread2.Start();20 thread3.Start();21 2223 private int cnt = 0;24 private void Count()25 26 while( cnt app.Width ) dx = -dx;39 if( yapp.Height) dy = -dy;40 Graphic
9、s g = app.CreateGraphics();4142 switch( type )43 44 case 0:45 g.FillRectangle( brush, x,y,w,h );46 g.DrawRectangle( pen, x,y,w,h );47 break;48 case 1: 10-749 g.FillEllipse( brush, x,y,w,h );50 g.DrawEllipse( pen, x,y,w,h );51 break;52 case 2: 53 g.FillPie( brush, x,y,w,h, 0.1F, 0.9F);54 g.DrawArc( p
10、en, x,y,w,h, 0.1F, 0.9F );55 break;56 57/Console.WriteLine(x+“,“+y+“,“+w+“,“+h+“:“+type+“,“+dx+“,“+dy);58 Thread.Sleep(130);5960 61 62 6364 /.65 private ArrayList threads = new ArrayList();66 void AddMovingObject()67 68 MovingShape obj = new MovingShape( this.pictureBox1 );69 Thread thread = new Thr
11、ead( new ThreadStart( obj.run ) );70 thread.IsBackground = true;71 thread.Start();72 threads.Add( thread );73 7475 private void Form1_Load(object sender, System.EventArgs e)76 77 AddMovingObject();78 7980 private void button1_Click(object sender, System.EventArgs e)81 82 AddMovingObject();83 8485 pr
12、ivate void button2_Click(object sender, System.EventArgs e)86 87 foreach( Thread thread in threads )88 89 thread.Suspend();90 91 10-89293 private void button3_Click(object sender, System.EventArgs e)94 95 foreach( Thread thread in threads )96 97 thread.Resume();98 99 运行结果如 图 10-2:图 10-2 多线程绘图10.1.3
13、线程的同步1线程同步的作用同步在多线程编程的非结构化性质与同步处理的结构化次序之间提供了一个折中的办法。使用同步技术,可以完成以下操作: (1)在必须以特定顺序执行任务时,显式地控制代码运行的次序。 (2)当两个线程同时共享相同的资源时,避免可能出现的问题。 例如,可以使用同步使显示过程处于等待状态,直至在另一线程中运行的数据检索过程结束。同步的方法有两种:轮询和使用同步对象。轮询是指循环地检查异步调用的状态(如反复查看 IsAlive 属性) ,这种方式效率很低,因为反复检查各种线程属性的状态会浪费大量资源。2Join 方法用轮询方式来控制运行线程的次序,牺牲了多线程的部分优点。为此,可以使
14、用效率较高的 Join 方法来控制线程。 Join 使调用过程处于等待状态,直至线程完成或调用超时(如果指定了超时) 。实际上,使用 Join 再次将单独的执行线程合并成一个线程。调用某 Thread 对象的 Join()方法,可以将一个线程加入到本线程中,本线程的执行会等待另一线程执行完毕。如果 Join()方法带上一个 int 参数,表示等待的最长时间(以毫秒10-9为单位) 。例 10-3 ThreadJoin.cs 使用 Join1 using System;2 using System.Threading;3 public class ThreadJoin 4 public stat
15、ic void Main(string args) 5 Runner r = new Runner();6 Thread thread = new Thread( new ThreadStart( r.run) );7 thread.Start();89 /thread.Join();1011 for( int i=0; i10; i+ )12 Console.WriteLine(“t“ + i );13 Thread.Sleep(100);14 15 16 1718 class Runner 19 public void run() 20 for( int i=0; i10; i+ )21
16、Console.WriteLine( i );22 Thread.Sleep(100);23 24 25 程序结果如 图 10-3。10-10(a)若不使用 Join() (b)使用 Join()图 10-3 使用 Join 对程序的影响程序中若没有用 Join(),则两个线程同时运行 (注意程序本身也是一个线程 );若将 Join()加上,则主程序的线程会等待 Runner 的线程执行完毕后再进行。Join 是同步调用或阻塞调用,这些控制线程的简单方法在主要用于管理少量线程,不适合于复杂情况。下面将讨论控制同步线程的一些高级技术。3lock 语句与 Monitor 类在多个线程访问同一资源时
17、,可能会发生数据不一致的情况。例如下面的例子。例 10-4 两个线程访问同一资源时的问题。1 using System;2 using System.Threading;3 class SyncCounter24 5 public static void Main(string args) 6 Num num = new Num();7 Thread thread1 = new Thread( new ThreadStart(num.run);8 Thread thread2 = new Thread( new ThreadStart(num.run);9 thread1.IsBackground = true;10 thread2.IsBackground = true;11 thread1.Start();12 thread2.Start();13 for( int i=0; i10; i+ )14 Thread.Sleep(100);