利用委托实现异步

步骤

(1)同步方法

(2)定义类型与同步方法一致的委托

(3)定义委托变量,与同步方法绑定

(4)调用委托的BeginInvoke()和EndInvoke(),从而实现异步调用同步方法的效果


 

public IAsyncResult BeginInvoke(int p1,out double p2,ref bool p3,AsyncCallBack callback,object AsyncState)
  1. 头几个参数是同步方法的参数
  2. 倒数第二个参数是一个委托,类型是 delegate void AsyncCallBack(IAsyncResult ia);如果被封装的同步方法异步执行结束后,想自动调用某个方法CallbackMethod,则可以将该方法作为倒数第二个参数的实参;如果不需要回调任何方法,则倒数第二个参数为null。
  3. 最后一个参数是异步方法和回调方法之间通信的媒介。如果异步方法无回调函数,则最后一个参数也为null;若有回调函数,那么,如果回调函数需要什么参数,可以打包成一个对象,作为倒数最后一个参数的实参。回调函数内部使用时,需要先进行类型转换。
  4. 返回一个结果,由接口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 )
  1. 头几个参数是同步方法的参数,最后一个参数是BeginInvoke()的返回值。
  2. EndInvoke()的返回值是异步方法的返回值,假设异步方法的返回值是int,则 int res = EndInvoke();如果异步方法无返回值,直接EndInvok();即可。
  3. EndInvoke()是阻塞式的,如果异步方法尚未完成,则会阻塞主调线程,直到异步方法执行完毕。
  4. EndInvoke()会在线程池中清理完成的异步方法,所以务必只要调用了BeginInvoke(),就要调用EndInvoke();
  5. 如果异步方法在执行过程中抛出异常,会在EndInvoke()抛出,所以,如果异步方法可能抛出异常,在EndInvoke()中捕获即可。
  6. 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的原理。

(1)
Thread ----> Fun(int a);
等价于
Action<int> delegateVar += Fun;
delegateVar.BeginInvoke(10,null,null);
可以在回到方法中执行EndInvoke();
也可以在主调方法中执行EndInvoke();
(2)
Thread ----> while(true) { Fun(int a); }
等价于
把Fun封装成异步方法,回调函数中再次调用Fun。
posted @ 2019-03-11 21:00  LiWeiAiLa  阅读(941)  评论(0编辑  收藏  举报