天天看点

Delegate的BeginInvoke()

Delegate的BeginInvoke()

谈到异步操作,就不得不说异步委托,可以说异步委托在实现异步操作方面可谓有得天独厚的优势。那么什么是异步委托呢?异步委托就是定义一个方法,开一个新线程,让这个方法在后台执行。委托是方法的类型安全引用。Delegate类支持异步调用的方法,在后台Delegate类会创建一个执行任务的线程。

在.NET平台下,主要通过委托的BeginInvoke()来实现异步操作。

下面我会先通过一个小例子给大家进行解析。

首先创建一个委托

Public delegate void SayHello(string name);

然后在程序的主函数里写下如下代码:

static void Main(string[] args)
        {
            Console.WriteLine("主线程的ID:" + Thread.CurrentThread.ManagedThreadId);//标记显示主线程ID
            SayHello sayhello = new SayHello(Say);//新建一个委托对象
            sayhello.BeginInvoke("Olive", null, null);//调用委托的异步函数BeginInvoke(),这里边三个参数,第一个参数,为所调用的函数传递的参数,第二、第三个会在下边慢慢介绍
 
        private static void Say(string name)
        {
            Console.WriteLine("Hello!--------" + name);
            Console.WriteLine("当前的线程ID为:" + Thread.CurrentThread.ManagedThreadId);//显示当前函数执行时所在线程
        }
           

实验结果如下:

Delegate的BeginInvoke()

通过这个简单的小例子我们可以发现:通过委托所实现的异步,不过是又启用了一个新的线程,但是大家要注意的是这个线程是线程池里的线程,是属于后台线程的。

下面我们继续,刚刚我们做的例子是不需要返回结果的异步操作,而实际的开发中,往往需要有返回的结果。那该如何做呢?

首先我们还是要先建一个委托:

 delegate string SayHello(string name);

在主函数中代码如下:

static void Main(string[] args)
        {
            Console.WriteLine("主线程的ID:" + Thread.CurrentThread.ManagedThreadId);//标记显示主线程ID
            SayHello sayhello = new SayHello(Say);
            IAsyncResult iResult=sayhello.BeginInvoke("Olive", null, null);//Delegate.BeginInvoke()方法实际上是一个IAsyncResult的对象
            string result = sayhello.EndInvoke(iResult);//Delegate.EndInvoke()方法就是返回调用函数的返回值,但是该方法需要一个IAsyncResult对象,也就是委托调用BeginInvoke()开始异步
            Console.WriteLine(result);
            Console.ReadKey();
        }
        private static string  Say(string name)
        {
            Console.WriteLine("Hello!--------" + name);
            Console.WriteLine("当前的线程ID为:" + Thread.CurrentThread.ManagedThreadId);
            return "I Love you " + name;
        }
           

实验结果如下:

Delegate的BeginInvoke()

但是这个时候又有一个问题出现了,刚刚我们所举的异步操作只是很简单的返回一句话而已,如果是非常耗时的异步操作的话,我们也不知道操作有没有结束,如果在未结束时调用Delegate.EndInvoke()会一直的处在阻塞状态。这个时候该怎么办呢?

可以有三种办法解决这个问题:

第一个就是反复的询问

只需要在主函数里做如下修改即可:

static void Main(string[] args)
        {
            Console.WriteLine("主线程的ID:" + Thread.CurrentThread.ManagedThreadId);//标记显示主线程ID
            SayHello sayhello = new SayHello(Say);
            IAsyncResult iResult=sayhello.BeginInvoke("Olive", null, null);//Delegate.BeginInvoke()方法实际上是一个IAsyncResult的对象
           If(iResult.IsCompleted)
            {           
 	     string result =  sayhello.EndInvoke(iResult);//Delegate.EndInvoke()方法就是返回调用函数的返回值,但是该方法需要一个IAsyncResult对象,也就是委托调用BeginInvoke()开始异步
             Console.WriteLine(result);
            } 
            Console.ReadKey();
        }
           

第二个方法:使用IAsyncReault相关联的等待句柄,使用AsyncWaitHandle属性就可以访问等待句柄。这个属性返回WaitHandle类型的对象,它可以等待委托线程完成其任务。WaitOne()方法将一个超时时间作为可选的第一个参数,在其中可以定义要等待的最长时间。如果发生超时WaitOne方法就返回false

if (iResult.AsyncWaitHandle.WaitOne(1000,false)) //如果等待超过1秒,则无法返回结果
 
            {
                string result = sayhello.EndInvoke(iResult);
                Console.WriteLine(result);
            }
           

第三种方法:异步回调。这个方法是最为推荐的方法。

那么什么是异步回调呢?或许刚刚大家有注意到在调用Delegate.BeginInvoek()方法时,只有有三个参数BeginInvoke(object parameter,AsyncCallback asynccallback,object state),上面只讲第一个参数,也就是委托调用的有参函数所需的参数,多个参数也可以,只需要按照委托声明时的顺序依次添加就可以了,第二个是一个AsyncCallBack类型的委托也就是异步操作执行完后要执行的委托函数,比如需要返回结果、输出什么的都可以在这个委托函数里执行,该委托函数必须有一个IAsyncResult类型的参数。第三个是额外的状态参数,一般都是该委托对象。

下面通过实例给大家进行讲解。

第一步还是先建立一个委托

delegate string  SayHello(string name);

主函数:

static void Main(string[] args)
        {
   Console.WriteLine("主线程的ID:" + Thread.CurrentThread.ManagedThreadId);//标记显示主  线程ID
            SayHello sayhello = new SayHello(Say);
            IAsyncResult iResult=sayhello.BeginInvoke("Olive",new AsyncCallback(Result), sayhello);//三个参数:1、Say()函数的参数,2、AsyncCallback类型的委托,(异步操作结束后执行的委托函数),3、将sayhello对象作为状态参数,在Result函数中会有用到该参数
     Console.ReadKey();
        }
         //AsyncCallback委托所执行的函数
        private static void Result(IAsyncResult iasyncresult)
        {
            SayHello sayhello = (SayHello)iasyncresult.AsyncState;//获取IAsyncResult对象的AsyncState的属性,即为Delegate.BeginInvoke()的第三个参数即sayhello。
            string s = sayhello.EndInvoke(iasyncresult);
            Console.WriteLine(s + "------好了,异步调用到这里已经结束了");
        }
        private static string  Say(string name)
        {
            Console.WriteLine("Hello!--------" + name);
            Console.WriteLine("当前的线程ID为:" + Thread.CurrentThread.ManagedThreadId);
            return "I Love you " + name;
        }
           

实验结果如图:

Delegate的BeginInvoke()

至此,本节的异步操作已经结束了,在实际的运用中异步操作用的还是比较多的,建议大家在使用异步操作的时候多用下异步回调,这种方法效率比较高、也不会发生异步的阻塞。