zoukankan      html  css  js  c++  java
  • [C#基础]c#中的BeginInvoke和EndEndInvoke

    摘要

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

    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可以做以下工作:

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

    注意

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

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

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

    using System;
    using System.Diagnostics;
    using System.Threading;
    
    namespace BeginInvokeDemo
    {
        /// <summary>
        /// 委托必须和要调用的异步方法有相同的签名
        /// </summary>
        /// <param name="callDuration">sleep时间</param>
        /// <param name="threadId">当前线程id</param>
        /// <returns></returns>
        public delegate string AsyncMethodCaller(int callDuration, out int threadId);
        class Program
        {
    
            /// <summary>
            /// 主函数
            /// </summary>
            /// <param name="args"></param>
            static void Main(string[] args)
            {
                AsyncMethodCaller caller = new AsyncMethodCaller(TestMethodAsync);
                int threadid = 0;
                //开启异步操作
                IAsyncResult result = caller.BeginInvoke(3000, out threadid, null, null);
                for (int i = 0; i < 10; i++)
                {
                    Console.WriteLine("其它业务" + i.ToString());
                }
                //调用EndInvoke,等待异步执行完成
                Console.WriteLine("等待异步方法TestMethodAsync执行完成");
                string res = caller.EndInvoke(out threadid, result);
                Console.WriteLine("Completed!");
                Console.WriteLine(res);
                Console.Read();
            }
            /// <summary>
            /// 与委托对应的方法
            /// </summary>
            /// <param name="callDuration"></param>
            /// <param name="threadId"></param>
            /// <returns></returns>
            static string TestMethodAsync(int callDuration, out int threadId)
            {
                Stopwatch sw = new Stopwatch();
                sw.Start();
                Console.WriteLine("异步TestMethodAsync开始");
                for (int i = 0; i < 5; i++)
                {   // 模拟耗时操作
                    Thread.Sleep(callDuration);
                    Console.WriteLine("TestMethodAsync:" + i.ToString());
                }
                sw.Stop();
                threadId = Thread.CurrentThread.ManagedThreadId;
                return string.Format("耗时{0}ms.", sw.ElapsedMilliseconds.ToString());
            }
        }
    }
    View Code

    结果

    由上图,可以看出,在BeginInvoke开启异步执行方法,会先执行其他的业务。通过EndInvoke方法,阻塞直到异步执行完毕,才会执行EndInvoke之后的代码。

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

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

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

    注意

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

    using System;
    using System.Diagnostics;
    using System.Threading;
    
    namespace BeginInvokeDemo
    {
        /// <summary>
        /// 委托必须和要调用的异步方法有相同的签名
        /// </summary>
        /// <param name="callDuration">sleep时间</param>
        /// <param name="threadId">当前线程id</param>
        /// <returns></returns>
        public delegate string AsyncMethodCaller(int callDuration, out int threadId);
        class Program
        {
    
            /// <summary>
            /// 主函数
            /// </summary>
            /// <param name="args"></param>
            static void Main(string[] args)
            {
                AsyncMethodCaller caller = new AsyncMethodCaller(TestMethodAsync);
                int threadid = 0;
                //开启异步操作
                IAsyncResult result = caller.BeginInvoke(3000, out threadid, null, null);
                for (int i = 0; i < 10; i++)
                {
                    Console.WriteLine("其它业务" + i.ToString());
                }
                //调用EndInvoke,等待异步执行完成
                Console.WriteLine("等待异步方法TestMethodAsync执行完成");
                //等待异步执行完毕信号
                result.AsyncWaitHandle.WaitOne();
                Console.WriteLine("收到WaitHandle信号");
                //通过EndInvoke检查结果
                string res = caller.EndInvoke(out threadid, result);
                //显示关闭句柄
                result.AsyncWaitHandle.Close();
                Console.WriteLine("关闭了WaitHandle句柄");
                Console.WriteLine("Completed!");
                Console.WriteLine(res);
                Console.Read();
            }
            /// <summary>
            /// 与委托对应的方法
            /// </summary>
            /// <param name="callDuration"></param>
            /// <param name="threadId"></param>
            /// <returns></returns>
            static string TestMethodAsync(int callDuration, out int threadId)
            {
                Stopwatch sw = new Stopwatch();
                sw.Start();
                Console.WriteLine("异步TestMethodAsync开始");
                for (int i = 0; i < 5; i++)
                {   // 模拟耗时操作
                    Thread.Sleep(callDuration);
                    Console.WriteLine("TestMethodAsync:" + i.ToString());
                }
                sw.Stop();
                threadId = Thread.CurrentThread.ManagedThreadId;
                return string.Format("耗时{0}ms.", sw.ElapsedMilliseconds.ToString());
            }
        }
    }

    输出

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

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

    using System;
    using System.Diagnostics;
    using System.Threading;
    
    namespace BeginInvokeDemo
    {
        /// <summary>
        /// 委托必须和要调用的异步方法有相同的签名
        /// </summary>
        /// <param name="callDuration">sleep时间</param>
        /// <param name="threadId">当前线程id</param>
        /// <returns></returns>
        public delegate string AsyncMethodCaller(int callDuration, out int threadId);
        class Program
        {
    
            /// <summary>
            /// 主函数
            /// </summary>
            /// <param name="args"></param>
            static void Main(string[] args)
            {
                AsyncMethodCaller caller = new AsyncMethodCaller(TestMethodAsync);
                int threadid = 0;
                //开启异步操作
                IAsyncResult result = caller.BeginInvoke(1000, out threadid, null, null);
                for (int i = 0; i < 10; i++)
                {
                    Console.WriteLine("其它业务" + i.ToString());
                }
                //调用EndInvoke,等待异步执行完成
                Console.WriteLine("等待异步方法TestMethodAsync执行完成");
                //等待异步执行完毕信号
                //result.AsyncWaitHandle.WaitOne();
                //Console.WriteLine("收到WaitHandle信号");
                //通过循环不停的检查异步运行状态
                while (result.IsCompleted==false)
                {
                    Thread.Sleep(100);
                    Console.WriteLine("异步方法,running........");
                }
                //异步结束,拿到运行结果
                string res = caller.EndInvoke(out threadid, result);
                //显示关闭句柄
                result.AsyncWaitHandle.Close();
                Console.WriteLine("关闭了WaitHandle句柄");
                Console.WriteLine("Completed!");
                Console.WriteLine(res);
                Console.Read();
            }
            /// <summary>
            /// 与委托对应的方法
            /// </summary>
            /// <param name="callDuration"></param>
            /// <param name="threadId"></param>
            /// <returns></returns>
            static string TestMethodAsync(int callDuration, out int threadId)
            {
                Stopwatch sw = new Stopwatch();
                sw.Start();
                Console.WriteLine("异步TestMethodAsync开始");
                for (int i = 0; i < 5; i++)
                {   // 模拟耗时操作
                    Thread.Sleep(callDuration);
                    Console.WriteLine("TestMethodAsync:" + i.ToString());
                }
                sw.Stop();
                threadId = Thread.CurrentThread.ManagedThreadId;
                return string.Format("耗时{0}ms.", sw.ElapsedMilliseconds.ToString());
            }
        }
    }
    View Code

    输出

    其它业务0
    其它业务1
    其它业务2
    其它业务3
    其它业务4
    其它业务5
    其它业务6
    其它业务7
    其它业务8
    其它业务9
    等待异步方法TestMethodAsync执行完成
    异步TestMethodAsync开始
    异步方法,running........
    异步方法,running........
    异步方法,running........
    异步方法,running........
    异步方法,running........
    异步方法,running........
    异步方法,running........
    异步方法,running........
    TestMethodAsync:0
    异步方法,running........
    异步方法,running........
    异步方法,running........
    异步方法,running........
    异步方法,running........
    异步方法,running........
    异步方法,running........
    异步方法,running........
    异步方法,running........
    异步方法,running........
    TestMethodAsync:1
    异步方法,running........
    异步方法,running........
    异步方法,running........
    异步方法,running........
    异步方法,running........
    异步方法,running........
    异步方法,running........
    异步方法,running........
    异步方法,running........
    异步方法,running........
    TestMethodAsync:2
    异步方法,running........
    异步方法,running........
    异步方法,running........
    异步方法,running........
    异步方法,running........
    异步方法,running........
    异步方法,running........
    异步方法,running........
    异步方法,running........
    异步方法,running........
    TestMethodAsync:3
    异步方法,running........
    异步方法,running........
    异步方法,running........
    异步方法,running........
    异步方法,running........
    异步方法,running........
    异步方法,running........
    异步方法,running........
    异步方法,running........
    异步方法,running........
    TestMethodAsync:4
    异步方法,running........
    关闭了WaitHandle句柄
    Completed!
    耗时5031ms.

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

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

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

    using System;
    using System.Diagnostics;
    using System.Threading;
    
    namespace BeginInvokeDemo
    {
        /// <summary>
        /// 委托必须和要调用的异步方法有相同的签名
        /// </summary>
        /// <param name="callDuration">sleep时间</param>
        /// <param name="threadId">当前线程id</param>
        /// <returns></returns>
        public delegate string AsyncMethodCaller(int callDuration, out int threadId);
        class Program
        {
    
            /// <summary>
            /// 主函数
            /// </summary>
            /// <param name="args"></param>
            static void Main(string[] args)
            {
                AsyncMethodCaller caller = new AsyncMethodCaller(TestMethodAsync);
                int threadid = 0;
                //开启异步操作
                IAsyncResult result = caller.BeginInvoke(1000, out threadid, callBackMethod, caller);
                for (int i = 0; i < 10; i++)
                {
                    Console.WriteLine("其它业务" + i.ToString());
                }
                //调用EndInvoke,等待异步执行完成
                Console.WriteLine("等待异步方法TestMethodAsync执行完成");
                //等待异步执行完毕信号
                //result.AsyncWaitHandle.WaitOne();
                //Console.WriteLine("收到WaitHandle信号");
                //通过循环不停的检查异步运行状态
                //while (result.IsCompleted==false)
                //{
                //    Thread.Sleep(100);
                //    Console.WriteLine("异步方法,running........");
                //}
                //异步结束,拿到运行结果
                //string res = caller.EndInvoke(out threadid, result);
                ////显示关闭句柄
                //result.AsyncWaitHandle.Close();
                Console.WriteLine("关闭了WaitHandle句柄");
    
                //Console.WriteLine(res);
                Console.Read();
            }
            /// <summary>
            /// 异步方法回调方法,异步执行完毕,会回调该方法
            /// </summary>
            /// <param name="ar"></param>
            private static void callBackMethod(IAsyncResult ar)
            {
                AsyncMethodCaller caller = ar.AsyncState as AsyncMethodCaller; 
                string result = caller.EndInvoke(out int threadid, ar);
                Console.WriteLine("Completed!");
                Console.WriteLine(result);
    
            }
    
            /// <summary>
            /// 与委托对应的方法
            /// </summary>
            /// <param name="callDuration"></param>
            /// <param name="threadId"></param>
            /// <returns></returns>
            static string TestMethodAsync(int callDuration, out int threadId)
            {
                Stopwatch sw = new Stopwatch();
                sw.Start();
                Console.WriteLine("异步TestMethodAsync开始");
                for (int i = 0; i < 5; i++)
                {   // 模拟耗时操作
                    Thread.Sleep(callDuration);
                    Console.WriteLine("TestMethodAsync:" + i.ToString());
                }
                sw.Stop();
                threadId = Thread.CurrentThread.ManagedThreadId;
                return string.Format("耗时{0}ms.", sw.ElapsedMilliseconds.ToString());
            }
        }
    }

    总结

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

  • 相关阅读:
    Android layout属性大全
    如何看懂Code128条形码
    二维码
    在线条形码生成器
    GS1已分配给国家(地区)编码组织的前缀码
    POJ 3321 Apple Tree DFS序+fenwick
    bootstrap之WaitForIdle&amp;&amp;Clear
    ubuntu14操作系统chrome标签和书签乱码解决
    动态规划-hdoj-4832-百度之星2014初赛第二场
    截取符合指数分布的一部分样本的理论与实验
  • 原文地址:https://www.cnblogs.com/wolf-sun/p/5675791.html
Copyright © 2011-2022 走看看