zoukankan      html  css  js  c++  java
  • 委托的BeginInvoke和EndInvoke方法

      .NET Framework 允许异步调用任何方法,为了实现异步调用目标,需要定义与被调用方法具有相同签名的委托。公共语言运行时会自动使用适当的签名为该委托定义 BeginInvoke 和 EndInvoke 方法,也就是说委托的 BeginInvoke 和 EndInvoke 方法是自动生成的,无需定义。所谓的异步调用,指的是在新线程中执行被调用的方法。
      BeginInvoke 方法启动异步调用, 该方法与要异步执行的方法具有相同的参数,还有另外两个可选参数。第一个参数是一个 AsyncCallback 委托,该委托引用在异步调用完成时要调用的方法。 第二个参数是一个用户定义的对象,该对象将信息传递到回调方法。BeginInvoke 立即返回,不等待异步调用完成。 BeginInvoke 返回一个 IAsyncResult,后者可用于监视异步调用的进度。
      采用BeginInvoke 和 EndInvoke实现方法的异步调用共有四种方式,下面用演示代码分别说明。代码示例演示异步调用一个长时间运行的方法 TestMethod 的各种方式。 TestMethod 方法会显示一条控制台消息,说明该方法已开始处理,方法所在的线程,休眠了几秒钟,然后结束。 TestMethod 有一个 out 参数,用于说明此类参数添加到 BeginInvoke 和EndInvoke 的签名中的方式,可以按同样的方式处理 ref 参数。

            public static string TestMethod(int callDuration, out int threadId)
            {
                Console.WriteLine("方法的执行线程ID: {0}", Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(callDuration);
                threadId = Thread.CurrentThread.ManagedThreadId;
                return String.Format("My call time was {0}.", callDuration.ToString());
            }

      定义相应的委托类型

     public delegate string AsyncDelegate(int  callDuration, out int threadId);

    1 使用 EndInvoke阻塞调用线程,直到异步调用结束

      异步执行方法的最简单方式是通过调用委托的 BeginInvoke 方法来开始执行方法,可以在在调用线程上执行一些其他操作,然后调用委托的 EndInvoke 方法。 EndInvoke 会阻塞调用线程,直到异步调用完成后才返回。

       

    namespace TestInvoke
    {
        public delegate string AsyncDelegate(int  callDuration, out int threadId);
        class Program
        {
            static void Main(string[] args)
            {
                //输出主线程ID
                Console.WriteLine("主线程ID:{0}", Thread.CurrentThread.ManagedThreadId);
                //创建委托
                AsyncDelegate asyncDel = new AsyncDelegate(TestMethod);
                int nThreadID = 0;
    
                //异步执行TestMethod方法
                IAsyncResult result = asyncDel.BeginInvoke(3000, out nThreadID, null, null);
                //阻塞调用线程
                asyncDel.EndInvoke(out nThreadID, result);
    
    
                Console.ReadKey();
            }
    
            public static string TestMethod(int callDuration, out int threadId)
            {
                Console.WriteLine("方法的执行线程ID: {0}", Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(callDuration);
                threadId = Thread.CurrentThread.ManagedThreadId;
                return String.Format("My call time was {0}.", callDuration.ToString());
            }
        }
    }

      调用线程会被阻塞3秒钟,程序输出为:

      

    2 使用 WaitHandle 等待异步调用

      使用由 BeginInvoke 返回的 IAsyncResult 的 AsyncWaitHandle 属性来获取 WaitHandle。 异步调用完成时, WaitHandle 会收到信号,可以通过调用 WaitOne 方法等待它。

      如果您使用 WaitHandle时,在调用 EndInvoke 检索结果之前,还可以执行其他处理。

      

            static void Main(string[] args)
            {
                //输出主线程ID
                Console.WriteLine("主线程ID:{0}", Thread.CurrentThread.ManagedThreadId);
                //创建委托
                AsyncDelegate asyncDel = new AsyncDelegate(TestMethod);
                int nThreadID = 0;
    
                //异步执行TestMethod方法
                IAsyncResult result = asyncDel.BeginInvoke(3000, out nThreadID, null, null);
                //阻塞调用线程
                WaitHandle handle = result.AsyncWaitHandle;
                handle.WaitOne();
    
                //其他操作
    
                //终止异步调用,通过返回值取得调用结果
                string returnValue = asyncDel.EndInvoke(out nThreadID, result);
    
                Console.WriteLine("The call executed on thread {0}, with return value "{1}".",
                    nThreadID, returnValue);
    
                Console.ReadKey();
            }

    3轮询异步调用完成

      可以使用由 BeginInvoke 返回的 IAsyncResult 的 IsCompleted 属性来判断异步调用何时完成。

            static void Main(string[] args)
            {
                //输出主线程ID
                Console.WriteLine("主线程ID:{0}", Thread.CurrentThread.ManagedThreadId);
                //创建委托
                AsyncDelegate asyncDel = new AsyncDelegate(TestMethod);
                int nThreadID = 0;
    
                //异步执行TestMethod方法
                IAsyncResult result = asyncDel.BeginInvoke(3000, out nThreadID, null, null);
    
                //轮询异步执行状态
                while (true)
                {
                    if (result.IsCompleted)
                    {
                        break;
                    }
                    Thread.Sleep(1000);
                }
    
                //终止异步调用,通过返回值取得调用结果
                string returnValue = asyncDel.EndInvoke(out nThreadID, result);
    
                Console.WriteLine("The call executed on thread {0}, with return value "{1}".",
                    nThreadID, returnValue);
    
                Console.ReadKey();
            }

     4 异步调用完成时执行回调方法 

      如果调用的线程不需要的异步执行函数的返回值,则可以在调用完成时执行回调方法,回调方法在 ThreadPool 里的异步线程上执行。

    若要使用回调方法,必须将表示回调方法的 AsyncCallback 委托传递给 BeginInvoke。 也可以传递包含回调方法要使用的信息的对象。在回调方法中,可以将 IAsyncResult(回调方法的唯一参数)强制转换为 AsyncResult 对象。 然后,可以使用AsyncResult .AsyncDelegate 属性获取已用于启动调用的委托,以便可以调用 EndInvoke。

    • TestMethod 的 threadId 参数为 out 参数,因此 TestMethod 从不使用该参数的输入值。  如果 threadId 参数为 ref 参数,则该变量必须为类级别字段,这样才能同时传递给 BeginInvoke 和 EndInvoke。

    • 传递给 BeginInvoke 的状态信息是一个格式字符串,所以状态信息必须强制转换为正确的类型才能被使用。

    • 回调在 ThreadPool 线程上执行。 ThreadPool 线程是后台线程,这些线程不会在主线程结束后保持应用程序的运行,因此示例的主线程必须休眠足够长的时间以便回调完成。

      

           static void Main(string[] args)
            {
                //输出主线程ID
                Console.WriteLine("主线程ID:{0}", Thread.CurrentThread.ManagedThreadId);
                //创建委托
                AsyncDelegate asyncDel = new AsyncDelegate(TestMethod);
                int nThreadID = 0;
    
                //异步执行TestMethod方法,使用回调函数并传入state参数
                IAsyncResult result = asyncDel.BeginInvoke(3000, out nThreadID,
                    new AsyncCallback(AsyncCallCompleted), "测试参数传递");
    
                Console.ReadKey();
            }
    
            public static void AsyncCallCompleted(IAsyncResult ar)
            {
                Console.WriteLine("AsyncCallCompleted执行线程ID:{0}",Thread.CurrentThread.ManagedThreadId);
    
                //获取委托对象
                System.Runtime.Remoting.Messaging.AsyncResult result = (System.Runtime.Remoting.Messaging.AsyncResult)ar;
                AsyncDelegate asyncDel = (AsyncDelegate)result.AsyncDelegate;
    
                //获取BeginInvoke传入的的state参数
                string strState = (string)ar.AsyncState;
                Console.WriteLine("传入的字符串是{0}",strState);
    
                //结束异步调用
                int nThreadID;
                string strResult = asyncDel.EndInvoke(out nThreadID, ar);
    
                Console.WriteLine("
    The call executed on thread {0}, with return value "{1}".",
                   nThreadID, strResult);
            }
    
            public static string TestMethod(int callDuration, out int threadId)
            {
                Console.WriteLine("TestMethod方法的执行线程ID: {0}", Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(callDuration);
                threadId = Thread.CurrentThread.ManagedThreadId;
                return String.Format("My call time was {0}.", callDuration.ToString());
            }

      输出结果为

      

     总结

        除了第4种采用回调函数的方式外,其他三种方式均会阻塞调用线程。

  • 相关阅读:
    fork和Vfork的区别
    exer4.13.c(undone)
    Exer4.6.c(undone)
    好习惯
    c语言中的register修饰符
    请教如何在页面之间传递dataSet?不用session
    ultraEdite编辑shell或perl程序时注意
    PowerBuilder程序中取数据库中值,值异常(正数变成负数或异常)
    pb程序的编译发布
    关于sql server2000 的1068 与 1069 问题
  • 原文地址:https://www.cnblogs.com/canger/p/5938591.html
Copyright © 2011-2022 走看看