(二)异步方法BeginInvoke和EndInvoke

时间:2022-08-27 18:42:15

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

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

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

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

  • 做一些其他操作,然后调用EndInvoke方法阻塞线程直到该方法完成。
  • 使用IAsyncResult.AsyncWaitHandle属性,使用它的WaitOne方法阻塞线程直到收到WaitHandle信号,然后调用EndInvoke。
  • 检查BeginInvoke返回值IAsyncResult的状态来决定方法是否完成,然后调用EndInvoke方法。
 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属性阻塞线程。

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();
        }

情况4、可以使用回调函数。

private delegate string RunOnThreadPool(out int threadId);

        private static void Callback(IAsyncResult ar)
        {
            Console.WriteLine("开始回调方法");
            Console.WriteLine("回调方法的参数: {0}", ar.AsyncState);
            Console.WriteLine("是否线程池: {0}", Thread.CurrentThread.IsThreadPoolThread);
            Console.WriteLine("线程池线程 id: {0}", Thread.CurrentThread.ManagedThreadId);
        }


        private static string Test(out int threadId)
        {
            Console.WriteLine("Starting...");
            Console.WriteLine("是否线程池: {0}", Thread.CurrentThread.IsThreadPoolThread);
            Thread.Sleep(TimeSpan.FromSeconds(2));
            threadId = Thread.CurrentThread.ManagedThreadId;
            return string.Format("线程池线程 id 是: {0}", threadId);
        }

(二)异步方法BeginInvoke和EndInvoke

 回调函数的执行,是在EndInvoke之后执行的。