zoukankan      html  css  js  c++  java
  • [转载]C#异步委托的用法 .

     每个委托都有三个方法:Invoke、BeginInvoke、EndInvoke。第一个方法是委托指定函数的同步调用,另外两个是异步调用。

            BeginInvoke方法,调用后立即返回,不等待调用结果。EndInvoke方法,用于检索调用结果。调用BeginInvoke后可随时调用 EndInvoke 方法;如果异步调用未完成,EndInvoke 将一直阻塞到异步调用完成。EndInvoke 的参数包括您需要异步执行的方法的 out 和 ref 参数(在 Visual Basic 中为 <Out> ByRef 和 ByRef)以及由 BeginInvoke 返回的 IAsyncResult

            BeginInvoke 异步方法签名的规则是:

    • 包括所有 IN 参数。
    • 包括所有 OUT 参数。
    • 包括所有 IN/OUT 参数。
    • 包括所有 ByRef 参数。
    • 将 AsyncCallback 和 AsyncState(可通过 IAsyncResult 接口的 AsyncState 属性获得)作为最后两个参数。
    • 返回 IAsyncResult。

            EndInvoke 异步方法签名的规则是:

    • 包括所有 IN/OUT 参数。
    • 包括所有 OUT 参数。
    • 包括所有 ByRef 参数。
    • 将 IAsyncResult 作为最后一个参数。
    • 从原始方法签名返回原始返回类型。

            结果对象 (IAsyncResult) 是从开始操作返回的,并且可用于获取有关异步开始操作是否已完成的状态。结果对象被传递到结束操作,该操作返回调用的最终返回值。在开始操作中可以提供可选的回调。如果提供回调,在调用结束后,将调用该回调;并且回调中的代码可以调用结束操作。


    AsyncCallback 委托

            AsyncCallback 委托用于指定在开始操作完成后应被调用的方法。下面是该委托的签名,AsyncCallback 委托被作为开始操作上的第二个到最后一个参数传递:

            public delegate void AsyncCallback(IAsyncResult ar);
    IAsyncResult 接口

            IAsyncResult 接口用于监视和管理异步操作。该接口是从开始操作返回的并被传递到结束操作,以将开始操作和结束操作相关联。如果回调被指定为开始操作的一部分,则 AsyncResult 被传递到回调。以下代码阐释有关 IAsyncResult 接口的属性,该接口可用于监视异步操作的状态并获取还可被传递到开始操作中的异步状态对象:


    public interface IAsyncResult

    {

      Object AsyncState { get; }                       //该属性为BeginInvoke参数中的最后一个参数对象

      WaitHandle AsyncWaitHandle { get; }

      bool CompletedSynchronously { get; }

      bool IsCompleted { get; }                         //该属性判断异步调用是否结束

    }

    • AsyncState

            返回在开始操作方法调用中作为最后一个参数提供的对象。

    • AsyncWaitHandle

            AsyncWaitHandle 属性返回 WaitHandle,后者可用于执行 WaitHandle.WaitOne、WaitAny 或 WaitAll。

            注意   直到 AsyncWaitHandle 属性被读取时,实现 IAsyncResult 的对象才需要创建 WaitHandle;执行时间由实施者决定。如果实施者创建了 WaitHandle,则实施者需要负责在适当的时候发出 WaitHandle 信号终止等待。例如,当异步调用的方法返回时,AsyncResult 将代表调用方终止等待。创建后,WaitHandle 应保持活动状态,直到用户调用结束异步操作的方法。此时,可以丢弃 AsyncWaitHandle 后的对象。

    • CompletedSynchronously

            如果开始操作调用已同步完成,则 CompletedSynchronously 属性将被设置为 true。

    • IsCompleted

            在服务器已处理完调用后,IsCompleted 属性将被设置为 true。

            调用了 BeginInvoke 后,可以:

    • 进行某些操作,然后调用 EndInvoke 一直阻塞到调用完成。
    • 使用 IAsyncResult.AsyncWaitHandle 获取 WaitHandle,使用它的 WaitOne 方法将执行一直阻塞到发出 WaitHandle 信号,然后调用 EndInvoke。
    • 轮询由 BeginInvoke 返回的 IAsyncResult,确定异步调用何时完成,然后调用 EndInvoke。
    • 将用于回调方法的委托传递给 BeginInvoke。该方法在异步调用完成后在 ThreadPool 线程上执行,它可以调用 EndInvoke。

            注: 始终在异步调用完成后调用 EndInvoke。

            以下是关于异步委托的测试代码:

    using System;

    using System.Threading;

     

    public class AsyncDemo {

        // The method to be executed asynchronously.

        //

        public string TestMethod(int callDuration, out int threadId) {

            Console.WriteLine("Test method begins.");

            Thread.Sleep(callDuration);

            threadId = AppDomain.GetCurrentThreadId();

            return "MyCallTime was " + callDuration.ToString();

        }

    }

     

    // The delegate must have the same signature as the method

    // you want to call asynchronously.

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

     

    public class AsyncMain {

        static void Main(string[] args) {

            // The asynchronous method puts the thread id here.

            int threadId;

     

            // Create an instance of the test class.

            AsyncDemo ad = new AsyncDemo();

     

            // Create the delegate.

            AsyncDelegate dlgt = new AsyncDelegate(ad.TestMethod);

      

            // Initiate the asychronous call.

            IAsyncResult ar = dlgt.BeginInvoke(3000,

                out threadId, null, null);

     

            Thread.Sleep(0);

            Console.WriteLine("Main thread {0} does some work.",

                AppDomain.GetCurrentThreadId());

     

            // Wait for the WaitHandle to become signaled.

            ar.AsyncWaitHandle.WaitOne();

    /*              
    这个是轮询异步执行状态

            // Poll while simulating work.

            while(ar.IsCompleted == false)

            {

                Thread.Sleep(10);

            }

    */

            // Call EndInvoke to Wait for the asynchronous call to complete,

            // and to retrieve the results.

            string ret = dlgt.EndInvoke(out threadId, ar);

     

            Console.WriteLine("The call executed on thread {0}, with return value \"{1}\".", threadId, ret);

        }

    }

     

            以下代码是用回调函数执行EndInvoke方法

    public class AsyncMain {

        // Asynchronous method puts the thread id here.

        private static int threadId;

     

        static void Main(string[] args) {

            // Create an instance of the test class.

            AsyncDemo ad = new AsyncDemo();

     

            // Create the delegate.

            AsyncDelegate dlgt = new AsyncDelegate(ad.TestMethod);

      

            // Initiate the asychronous call.  Include an AsyncCallback

            // delegate representing the callback method, and the data

            // needed to call EndInvoke.

            IAsyncResult ar = dlgt.BeginInvoke(3000,

                out threadId,

                new AsyncCallback(CallbackMethod),

                dlgt );

     

            Console.WriteLine("Press Enter to close application.");

            Console.ReadLine();

        }

       

            

        // Callback method must have the same signature as the

        // AsyncCallback delegate.

        static void CallbackMethod(IAsyncResult ar) {

            // Retrieve the delegate.

            AsyncDelegate dlgt = (AsyncDelegate) ar.AsyncState;

     

            // Call EndInvoke to retrieve the results.

            string ret = dlgt.EndInvoke(out threadId, ar);

     

            Console.WriteLine("The call executed on thread {0}, with return value \"{1}\".", threadId, ret);

        }

    }

    -----------------------------------------------------------------------

     

    当进行耗时的工作时,可以使用异步委托执行。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Runtime.Remoting.Messaging;
      
    namespace 线程
    {
        class Program
        {
            delegate int facHandler(int n);
            static int Fac(int n)
            {
                if (n == 1)
                    return 1;            
                System.Threading.Thread.Sleep(100);           
                return Fac(n - 1) * n;
            }
      
            static void callBack(IAsyncResult result)
            {
                //result是Fac()的返回值            
                //将AsyncDelegate强制转换为用户定义的委托。
                facHandler handler = (facHandler)((AsyncResult)result).AsyncDelegate;
                Console.WriteLine("结果:" + handler.EndInvoke(result));
                Console.WriteLine(result.AsyncState);
            }
      
            static void Main(string[] args)
            {
                facHandler f = new facHandler(Fac);
                //开异步方法开始执行
                IAsyncResult result = f.BeginInvoke(10, new AsyncCallback(callBack), "计算结束");
                Console.WriteLine("做其他事情先……");
                Console.Read();
            }
        }
    }

     ---------------------------------------------------------------

     

    C#委托的异步调用

    本文将主要通过“同步调用”、“异步调用”、“异步回调”三个示例来讲解在用委托执行同一个“加法类”的时候的的区别和利弊。

    首先,通过代码定义一个委托和下面三个示例将要调用的方法:

    /*添加的命名空间
    using System.Threading;
    using System.Runtime.Remoting.Messaging;
    */
        public delegate int AddHandler(int a,int b);
        
    public class 加法类
        {
            
    public static int Add(int a, int b)
            {
                Console.WriteLine("开始计算:" + a + "+" + b);
                Thread.Sleep(3000); //模拟该方法运行三秒
                Console.WriteLine("计算完成!");
                
    return a + b;
            }
        }

    同步调用

    委托的Invoke方法用来进行同步调用。同步调用也可以叫阻塞调用,它将阻塞当前线程,然后执行调用,调用完毕后再继续向下进行。

    public class 同步调用
    {
            
    static void Main()
            {
                Console.WriteLine("===== 同步调用 SyncInvokeTest =====");
                AddHandler handler = new AddHandler(加法类.Add);
                
    int result = handler.Invoke(12);

                Console.WriteLine("继续做别的事情。。。");

                Console.WriteLine(result);
                Console.ReadKey();
            }
            
    /*运行结果:
             ===== 同步调用 SyncInvokeTest =====
             开始计算:1+2
             计算完成!
             继续做别的事情。。。
             3       */
    }

    同步调用会阻塞线程,如果是要调用一项繁重的工作(如大量IO操作),可能会让程序停顿很长时间,造成糟糕的用户体验,这时候异步调用就很有必要了。

    异步调用

    异步调用不阻塞线程,而是把调用塞到线程池中,程序主线程或UI线程可以继续执行。
    委托的异步调用通过BeginInvoke和EndInvoke来实现。

    public class 异步调用
    {
            
    static void Main()
            {
                Console.WriteLine("===== 异步调用 AsyncInvokeTest =====");
                AddHandler handler = new AddHandler(加法类.Add);

                
    //IAsyncResult: 异步操作接口(interface)
                
    //BeginInvoke: 委托(delegate)的一个异步方法的开始
                IAsyncResult result = handler.BeginInvoke(12nullnull);

                Console.WriteLine("继续做别的事情。。。");

                
    //异步操作返回
                Console.WriteLine(handler.EndInvoke(result));
                Console.ReadKey();
            }
            
    /*运行结果:
             ===== 异步调用 AsyncInvokeTest =====
             继续做别的事情。。。
             开始计算:1+2
             计算完成!
             3       */
    }

    可以看到,主线程并没有等待,而是直接向下运行了。
    但是问题依然存在,当主线程运行到EndInvoke时,如果这时调用没有结束(这种情况很可能出现),这时为了等待调用结果,线程依旧会被阻塞。

     异步委托,也可以参考如下写法:

    Action<object> action=(obj)=>method(obj);
    action.BeginInvoke(obj,ar=>action.EndInvoke(ar),null);

    简简单单两句话就可以完成一部操作。

    异步回调

    用回调函数,当调用结束时会自动调用回调函数,解决了为等待调用结果,而让线程依旧被阻塞的局面。

    public class 异步回调
    {
            
    static void Main()
            {
                Console.WriteLine("===== 异步回调 AsyncInvokeTest =====");
                AddHandler handler = new AddHandler(加法类.Add);

                
    //异步操作接口(注意BeginInvoke方法的不同!)
                IAsyncResult result = handler.BeginInvoke(1,2,new AsyncCallback(回调函数),"AsycState:OK");
                
                Console.WriteLine("继续做别的事情。。。");
                Console.ReadKey();
            }

            
    static void 回调函数(IAsyncResult result)
            {      //result 是“加法类.Add()方法”的返回值

                
    //AsyncResult 是IAsyncResult接口的一个实现类,空间:System.Runtime.Remoting.Messaging
                
    //AsyncDelegate 属性可以强制转换为用户定义的委托的实际类。
                AddHandler handler = (AddHandler)((AsyncResult)result).AsyncDelegate;
                Console.WriteLine(handler.EndInvoke(result));
                Console.WriteLine(result.AsyncState);
            }
            
    /*运行结果:
            ===== 异步回调 AsyncInvokeTest =====
            开始计算:1+2
            继续做别的事情。。。
            计算完成!
            3
            AsycState:OK
                     
    */
    }

    我定义的委托的类型为AddHandler,则为了访问 AddHandler.EndInvoke,必须将异步委托强制转换为 AddHandler。可以在异步回调函数(类型为 AsyncCallback)中调用 MAddHandler.EndInvoke,以获取最初提交的 AddHandler.BeginInvoke 的结果。 

    问题:

    (1)int result = handler.Invoke(1,2);
    为什么Invoke的参数和返回值和AddHandler委托是一样的呢?
    答:
    Invoke方法的参数很简单,一个委托,一个参数表(可选),而Invoke方法的主要功能就是帮助你在UI线程上调用委托所指定的方法。Invoke方法首先检查发出调用的线程(即当前线程)是不是UI线程,如果是,直接执行委托指向的方法,如果不是,它将切换到UI线程,然后执行委托指向的方法。不管当前线程是不是UI线程,Invoke都阻塞直到委托指向的方法执行完毕,然后切换回发出调用的线程(如果需要的话),返回。
    所以Invoke方法的参数和返回值和调用他的委托应该是一致的。

    (2)IAsyncResult result = handler.BeginInvoke(1,2,null,null);

    BeginInvoke : 开始一个异步的请求,调用线程池中一个线程来执行,
    返回IAsyncResult 对象(异步的核心). IAsyncResult 简单的说,他存储异步操作的状态信息的一个接口,也可以用他来结束当前异步。
    注意: BeginInvoke和EndInvoke必须成对调用.即使不需要返回值,但EndInvoke还是必须调用,否则可能会造成内存泄漏。

    (3)IAsyncResult.AsyncState 属性:
    获取用户定义的对象,它限定或包含关于异步操作的信息。 例如:

    static void AddComplete(IAsyncResult result) 
    {   
          AddHandler handler = (AddHandler)result.AsyncState;    
          Console.WriteLine(handler.EndInvoke(result)); 
          。。。。。
    }

     

  • 相关阅读:
    react-native ios打包和Android打包
    用create-react-app创建的react项目怎么解决打包体积过大
    react native 新建项目运行报错
    微信分享等配置,微信授权失败讲解
    微信公众号的code,微信授权
    linux 递归删除指定文件
    mariaDB安装 无密码就可以登录、远程访问、及新版本初始化密码
    mysql 可重复插入、创建语句
    redis数据库入门
    Extjs 3.4 同值合并
  • 原文地址:https://www.cnblogs.com/fx2008/p/2246965.html
Copyright © 2011-2022 走看看