利用TcpListener和TcpClient类在同步方式下接收、发送数据以及监听客户端连接时,在操作没有完成之前一直处于阻塞状态,这对于接受、发送数据量不大的情况或者操作勇士较短的情况下是比较方便的。但是,对于执行完成时间可能较长的任务,如传送大文件等,使用同步操作可能就不太合适了,这种情况下,最好的办法是使用异步操作。
所谓异步操作方式,就是我们希望让某个工作开始以后,能在这个工作尚未完成的时候继续处理其他工作。就行我们(主线程)安排A(子线程A)负责处理客人来访时办理一系列登记手续。在同步工作方式下,如果没有人来访,A就只能一直在某个房间等待,而不能同时做其他工作,显然这种方式不利于并行处理。我们希望的是,没有人来访时,A不一定一直在这个房间等待,亦可以到别处继续做其他事,二八这个工作交给总控室人员完成,这里的总控室就是Windows操作系统本身。总控室如何及时通知A呢?可以让A先告诉总控室一个手机号F(callback需要的方法名F),以便有人来访时总控室可以立即电话通知A(callback)。这样一来,一旦有客人来访,总控室人员(委托)就会立即给A打电话(通过委托自动运行方法F),A接到通知后,再处理客人来访时需要的登记手续(在方法F中完成需要的工作)。
异步操作最大的优点是可以在一个操作没有完成之前同时进行其他的操作。.NET框架供了一种称为AsyncCallback(异步回调)的委托,该委托允许启动异步的功能,并在条件具备时调用提供的回调方法(是一种在操作或活动完成时由委托自动调用的方法),然后在这个方法中完成并结束未完成的工作。
使用异步TCP 应用编程时,除了套接字有对应的异步操作方式外,TcpListener和TcpClient类也提供了异步操作方法。
异步操作方式下,每个Begin方法都有一个匹配的End方法。在程序中例用Begin方法开始执行异步操作,然后又委托在条件具备时调用End方法完成并接受异步操作。
下表列出了TcpListener、TcpClient以及套接字提供的部分异步操作方法。
表 TcpListener和TcpClient及Socket提供的部分异步操作方法
1 EventWaitHandle类虽然我们可以利用异步操作并行完成一系列功能,但是现实中的很多工作是相互关联的,某些工作必须要等另一个工作完成后才能继续。这个问题就是异步操作中的同步问题。
EventWaitHandle 类用于在异步操作时控制线程间的同步,即控制一个或多个线程继续执行或者等待其他线程完成。考虑这样一种情况:假设有两个线程,一个是写线程,一个是读线程,两个线程是并行运行的。下面是实现代码:
using System; using System.Threading; class Program { private int n1, n2, n3; static void Main(string[] args) { Program p = new Program(); Thread t0 = new Thread(new ThreadStart(p.WriteThread)); Thread t1 = new Thread(new ThreadStart(p.ReadThread)); t0.Start(); t1.Start(); Console.ReadLine(); } private void WriteThread() { Console.WriteLine("t1"); n1 = 1; n2 = 2; n3 = 3; } private void ReadThread() { Console.WriteLine("{0}+{1}+{2}={3}", n1, n2, n3, n1 + n2 + n3); } }
运行这个程序,输出结果为:
t1
0+0+0=0;
按照一般的思维逻辑,读线程执行结果应该是1+2+3=6,可实际运行的结果却是0+0+0=0。显然读线程输出的内容是在写线程尚未写入新值之前得到的结果。如果把这个问题一般化,即某些工作是在线程内部完成的,同时启动多个线程后,我们无法准确判断线程内部处理这些工作的具体时间,而又希望保证一个线程完成某些工作后,另一个线程才能在这个基础上继续运行,最好的办法是什么呢?
这个问题实际上就是如何同步线程的问题。在System.Threading 命名空间中,有一个EventWaitHandle 类,它能够让操作系统通过发出信号完成多个线程之间的同步,需要同步的线程可以先阻塞当前线程,然后根据Windows 操作系统发出的信号,决定是继续阻塞等待其他工作完成,还是不再等待而直接继续执行。
本文涉及到的EventWaitHandle类提供的方法有:
Reset方法:将信号的状态设置为非终止状态,即不让操作系统发出信号,从而导致等待收到信号才能继续执行的线程阻塞。
Set方法:将信号的状态设置为非终止状态,即不让操作系统发出信号,从而导致等待收到信号才能继续执行的线程阻塞。
WaitOne方法:阻塞当前线程,等待操作系统为其发出信号,直到收到信号才解除阻塞。