[C#基础]c#中的BeginInvoke和EndEndInvoke

余生颓废 提交于 2021-02-12 15:25:36

摘要

异步这东西,真正用起来的时候,发现事情还是挺多的,最近在项目中用到了异步的知识,发现对它还是不了解,处理起来,走了不少弯路。觉得还是补一补还是很有必要的。

MSDN原文地址:https://msdn.microsoft.com/en-us/library/2e08f6yc(v=vs.110).aspx

正文

.Net framework可以让你异步调用任何方法。为达这样的目的,你可以定义一个与你要调用的方法的签名相同的委托。公共语言运行时将自动为该委托定义与签名相同的BeginInvok和EndInvoke方法。

异步委托调用BeginInvok和EndInvoke方法,但在.NET Compact Framework中并不支持。

BeginInvoke方法触发你的异步方法,它和你想要执行的异步方法有相同的参数。另外还有两个可选参数,第一个是AsyncCallback委托是异步完成的回调方法。第二个是用户自定义对象,该对象将传递到回调方法中。BeginInvoke立即返回并且不等待完成异步的调用(继续执行该下面的代码,不需要等待)。BeginInvoke返回IAsyncResult接口,可用于检测异步调用的过程。

通过EndInvoke方法检测异步调用的结果。如果异步调用尚未完成,EndInvoke将阻塞调用线程,直到它完成。EndInvoke参数包括out和ref参数。

下面代码演示使用BeginInvoke和EndInvoke进行异步调用的四种常见方式。在调用BeginInvoke可以做以下工作:

  • 做一些其他操作,然后调用EndInvoke方法阻塞线程直到该方法完成。
  • 使用IAsyncResult.AsyncWaitHandle属性,使用它的WaitOne方法阻塞线程直到收到WaitHandle信号,然后调用EndInvoke。
  • 检查BeginInvoke返回值IAsyncResult的状态来决定方法是否完成,然后调用EndInvoke方法。
  • 通过在BeginInvoke方法中传递该委托,在回调方法中调用该委托的EenInvoke方法。

注意

无论你怎么使用,都必须调用EndInvoke方法结束你的异步调用。

下面通过模拟一个耗时的操作,实现上面说的那四种情况。

复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Wolfy.AsyncDemo
{
    /// <summary>
    /// 委托必须和要调用的异步方法有相同的签名
    /// </summary>
    /// <param name="callDuration"></param>
    /// <param name="threadId"></param>
    /// <returns></returns>
    public delegate string AsyncMethodCaller(int callDuration, out int threadId);
    class Program
    {       
        static void Main(string[] args)
        {
            
        }
        static string TestMethod(int callDuration, out int threadId)
        {
            Console.WriteLine("Test method begins:");
            //睡一会 模拟耗时操作
            Thread.Sleep(callDuration);
            threadId = Thread.CurrentThread.ManagedThreadId;
            return string.Format("My call time was {0}.", callDuration.ToString());
        }
    }
}
复制代码

情况一:通过EndInovke阻塞线程,直到异步调用结束。

复制代码
static void Main(string[] args)
        {
            int threadId;
            AsyncMethodCaller caller = new AsyncMethodCaller(TestMethod);
            IAsyncResult result = caller.BeginInvoke(3000, out threadId, null, null);
            //调用EndInvoke方法,等待异步嗲用完成,并得到结果。
            string returnValue = caller.EndInvoke(out threadId, result);
            Console.WriteLine("The call executed on thread {0},with return value {1}.", threadId, returnValue);
            Console.Read();
        }
复制代码

结果

情况二:通过WaitHandle属性阻塞线程。

你可以获得BeginInvoke的返回值的WaitHandle,并使用它的AsyncWaitHanlde属性。WaitHandle信号异步完成时,你可以通过调用WaitOne方法等待。

如果你使用WaitHandle,你可以在之前或者异步调用完成后进行其他的操作。但之后必须使用EndInvoke检查结果。

注意

当你调用EndInvoke方法时,等待句柄并不会自动关闭。如果你释放等待处理的所有引用,当垃圾回收等待句柄是,系统资源将被释放。一旦你完成使用等待句柄,通过WaitHandle的close方法,一次性显示关闭,这时的垃圾回收效率更高。

复制代码
static void Main(string[] args)
        {
            int threadId;
            AsyncMethodCaller caller = new AsyncMethodCaller(TestMethod);
            IAsyncResult result = caller.BeginInvoke(3000, out threadId, null, null);
            Thread.Sleep(0);
            Console.WriteLine("Main thread {0} does some work.", Thread.CurrentThread.ManagedThreadId);
            //等待WaitHandle信号
            result.AsyncWaitHandle.WaitOne();
            //调用EndInvoke方法,等待异步嗲用完成,并得到结果。
            string returnValue = caller.EndInvoke(out threadId, result);
            //关闭等待句柄
            result.AsyncWaitHandle.Close();
            Console.WriteLine("The call executed on thread {0},with return value {1}.", threadId, returnValue);
            Console.Read();
        }
复制代码

情况三:检查BeginInvoke返回结果的状态。

可以通过BeginInvoke的返回结果的IsCompleted属性检查异步是否完成。你可以在异步没有完成的时候做其他的操作。

复制代码
static void Main(string[] args)
        {
            int threadId;
            AsyncMethodCaller caller = new AsyncMethodCaller(TestMethod);
            IAsyncResult result = caller.BeginInvoke(3000, out threadId, null, null);
            Thread.Sleep(0);
            Console.WriteLine("Main thread {0} does some work.", Thread.CurrentThread.ManagedThreadId);
            while (result.IsCompleted == false)
            {
                Thread.Sleep(250);
                Console.WriteLine("*");
            }
            //调用EndInvoke方法,等待异步嗲用完成,并得到结果。
            string returnValue = caller.EndInvoke(out threadId, result);
            //关闭等待句柄
            result.AsyncWaitHandle.Close();
            Console.WriteLine("The call executed on thread {0},with return value {1}.", threadId, returnValue);
            Console.Read();
        }
复制代码

情况四:通过在回调方法中。

如果需要在异步完成后需要做一些其他的操作,你可以在异步完成时执行一个回调方法。在该回调方法中做处理。

首先需要定义一个回调方法。

复制代码
static void Main(string[] args)
        {
            AsyncMethodCaller caller = new AsyncMethodCaller(TestMethod);
            // The threadId parameter of TestMethod is an out parameter, so
            // its input value is never used by TestMethod. Therefore, a dummy
            // variable can be passed to the BeginInvoke call. If the threadId
            // parameter were a ref parameter, it would have to be a class-
            // level field so that it could be passed to both BeginInvoke and 
            // EndInvoke.
            int dummy = 0;
            // Initiate the asynchronous call, passing three seconds (3000 ms)
            // for the callDuration parameter of TestMethod; a dummy variable 
            // for the out parameter (threadId); the callback delegate; and
            // state information that can be retrieved by the callback method.
            // In this case, the state information is a string that can be used
            // to format a console message.
            IAsyncResult result = caller.BeginInvoke(3000, out dummy, TestMethodCallback, "The call executed on thread {0},with return value {1}.");
            Console.Read();
        }
复制代码

总结

在项目中遇到的有参数又返回值的情况,如何在回调方法中拿到委托和传递的参数,当时卡这里了,后来查看情况四的情况,才得以解决。这里也再学习一下。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!