步骤
(1)同步方法
(2)定义类型与同步方法一致的委托
(3)定义委托变量,与同步方法绑定
(4)调用委托的BeginInvoke()和EndInvoke(),从而实现异步调用同步方法的效果
public IAsyncResult BeginInvoke(int p1,out double p2,ref bool p3,AsyncCallBack callback,object AsyncState)
- 头几个参数是同步方法的参数
- 倒数第二个参数是一个委托,类型是 delegate void AsyncCallBack(IAsyncResult ia);如果被封装的同步方法异步执行结束后,想自动调用某个方法CallbackMethod,则可以将该方法作为倒数第二个参数的实参;如果不需要回调任何方法,则倒数第二个参数为null。
- 最后一个参数是异步方法和回调方法之间通信的媒介。如果异步方法无回调函数,则最后一个参数也为null;若有回调函数,那么,如果回调函数需要什么参数,可以打包成一个对象,作为倒数最后一个参数的实参。回调函数内部使用时,需要先进行类型转换。
- 返回一个结果,由接口IAsyncResult引用指向。它相当于一个监控,当异步方法被调用,未等执行完毕就先返回,执行后续的代码。在需要的时候,可以通过监控来等待异步方法执行完毕或获取异步方法的运行结果。
// // 摘要: // 表示异步操作的状态。 [ComVisible(true)] public interface IAsyncResult { bool IsCompleted { get; } WaitHandle AsyncWaitHandle { get; } object AsyncState { get; } bool CompletedSynchronously { get; } }
IsCompleted:监控结果.IsCompleted;如果是true,表示异步方法已经执行完毕。
AsyncWaitHandle:调用它的WaitOne(int times)方法,可以让线程等待异步方法执行完成若干时间。
AsyncState就是BeginInvoke()的最后一个参数,是传递给回调函数的“信息”。
CompletedSynchronously:如果异步方法很短,可能在返回监控IAsyncResult之前就已经执行结束,这时候,虽然是异步调用,但是仍旧是同步完成的。此时该变量等于true。
public 返回值 EndInvoke(out double p2,ref bool p3,IAsyncResult ia )
- 头几个参数是同步方法的参数,最后一个参数是BeginInvoke()的返回值。
- EndInvoke()的返回值是异步方法的返回值,假设异步方法的返回值是int,则 int res = EndInvoke();如果异步方法无返回值,直接EndInvok();即可。
- EndInvoke()是阻塞式的,如果异步方法尚未完成,则会阻塞主调线程,直到异步方法执行完毕。
- EndInvoke()会在线程池中清理完成的异步方法,所以务必只要调用了BeginInvoke(),就要调用EndInvoke();
- 如果异步方法在执行过程中抛出异常,会在EndInvoke()抛出,所以,如果异步方法可能抛出异常,在EndInvoke()中捕获即可。
- EndInvoke()只需要传入out参数,其他参数无需传入。
示例:
(1)如果异步方法无回调函数,BeginInvoke()的最后两个参数设为null即可。
(2)如果异步方法有回调函数,且回调函数无需从外界获取额外的信息,则将回调方法写好,然后作为BeginInvoke()的倒数第二个参数即可;补充:虽然回调函数的参数是IAsyncResult ia,但 是无需将异步方法的BeginInvoke()的返回监控传入,只要将回调函数当做参数,异步方法完成后就会自动执行回调方法。
最后一个参数是null。
(3)如果异步方法有回调函数且需要外界“给”一些额外信息,我们可以把额外信息封装成一个对象如【元组】传入。在编写回调函数时,使用前先向下转型,然后进行使用。
class Program { public delegate void GetMessageDelegate(); static GetMessageDelegate GetMessageHnadle; static void Main(string[] args) { GetMessageHnadle += GetMessage; GetMessageHnadle.BeginInvoke(CallBackMethod, null); Console.Read(); } public static void CallBackMethod(IAsyncResult ia) { Console.WriteLine("i am callback"); } public static void GetMessage() { Console.WriteLine("Data was received successfully."); Thread.Sleep(1000); } }
class Program { public delegate void GetMessageDelegate(); static GetMessageDelegate GetMessageHnadle; static void Main(string[] args) { GetMessageHnadle += GetMessage; GetMessageHnadle.BeginInvoke(CallBackMethod, "我是异步方法传入回调函数的信息。"); Console.Read(); } public static void CallBackMethod(IAsyncResult ia) { Console.WriteLine("i am callback"); Console.WriteLine(ia.AsyncState as string); } public static void GetMessage() { Console.WriteLine("Data was received successfully."); Thread.Sleep(1000); } }
等待异步方法完成的三种方式
1.
while(ia.IsCompleted == false) { waiting.... }
2.
ia.AsyncWaitHandle.WaitOne(100);
3.
EndInvoke();
获取异步方法结果的地点
调用EndInvoke()需要委托和监控IAysncResult。
1.如果调用异步方法的主线程,需要获取异步的结果或等待异步方法的完成,那么可以在主调方法中调用EndInvoke();
2.如果不care异步方法是否执行结束,也无需等待其完成,可以在回调方法中调用EndInvoke();
class Program { public delegate string GetMessageDelegate(); static GetMessageDelegate GetMessageHnadle; static void Main(string[] args) { GetMessageHnadle += GetMessage; GetMessageHnadle.BeginInvoke(CallBackMethod, "我是异步方法传入回调函数的信息。"); Console.Read(); } public static void CallBackMethod(IAsyncResult ia) { AsyncResult ar = ia as AsyncResult; GetMessageDelegate del = ar.AsyncDelegate as GetMessageDelegate; Console.WriteLine(del.EndInvoke(ia)); Console.WriteLine("i am callback"); Console.WriteLine(ia.AsyncState as string); } public static string GetMessage() { Console.WriteLine("Data was received successfully."); Thread.Sleep(1000); return "异步方法执行完毕"; } }
尽管IAsyncResult接口没有委托对象的引用,而向下转型得到的AsyncResult类对象却有委托对象的引用。
番外
异步方法的回调函数的参数是IAsyncResult类型,只要将异步方法的函数名作为BeginInvoke()的倒数第二个参数即可,监控会自动传入回调函数的!在回调函数中,可以对监控类型转换,能够得到外界传递给回调函数的“信息”,异步方法的委托。
异步方法和回调函数是完全独立的,可以理解成语法糖。
其实不调用EndInvoke()时,异步方法可能已经执行完成,EndInvoke()只是取出运行结果,填充out参数和清理线程池而已。
开启一个线程,线程内容:检测标志位ContinueFlag,若为true,则一直循环执行方法M1。 等价于 ==》 利用M1的委托异步执行M1,M1的回调函数里面检测ContinueFlag,若为true,继续调用M1。
这就是C#异步服务器的API的原理。