zoukankan      html  css  js  c++  java
  • 异步调用 (转载)

     

    异步操作通常用于执行完成时间可能较长的任务,如打开大文件、连接远程计算机或查询数据库。异步操作在主应用程序线程以外的线程中执行。应用程序调用方法异步执行某个操作时,应用程序可在异步方法执行其任务时继续执行。

    .NET框架能够对任何方法进行异步调用。进行异步调用时,需要定义与异步调用的方法具有相同签名的委托。公共语言运行时会自动使用适当的签名为该委托定义BeginInvokeEndInvoke方法。

    BeginInvoke方法用于启动异步调用。它与需要异步执行的方法具有相同的参数。此外,它还有两个可选参数。第一个参数是一个AsyncCallback委托,该委托引用在异步调用完成时要调用的方法。第二个参数是一个用户定义的对象,该对象可向回调方法传递数据。BeginInvoke立即返回,不会等待异步调用完成,被调用的方法将在线程池线程中执行。因此,提交请求的原始线程与执行异步方法的线程池线程是并行执行的。BeginInvoke会返回一个IAsyncResult对象,可以使用该对象来监视异步调用进度,也可将该对象传递给EndInvoke方法,以获取异步执行的方法的返回值。

    EndInvoke方法用于检索异步调用的结果。调用BeginInvoke方法后可随时调用EndInvoke方法;如果异步调用尚未完成,EndInvoke方法将一直阻塞调用线程,直到异步调用完成后才允许调用线程执行。EndInvoke方法的参数包括需要异步执行的方法的outref参数,以及由BeginInvoke返回的IAsyncResult对象。因此,通过EndInvoke方法可以获得异步调用的方法的所有输出数据,包括返回值、outref参数。

    AsyncCallback委托表示在异步操作完成时调用的回调方法,其定义如下:

    public delegate void AsyncCallback(IAsyncResult ar);

    System.IAsyncResult接口表示异步操作的状态。IAsyncResult接口由包含可异步操作的方法的类实现。它是启动异步操作的方法的返回类型,也是结束异步操作的方法的第三个参数的类型。当异步操作完成时,IAsyncResult对象也将传递给由AsyncCallback委托调用的方法。支持IAsyncResult接口的对象存储异步操作的状态信息,并提供同步对象以允许线程在操作完成时终止。IAsyncResult接口定义了四个公开属性,通过它们可以获取异步调用的状态。

    —  AsyncState:获取用户定义的对象,它限定或包含关于异步操作的信息。

    —  AsyncWaitHandle:获取用于等待异步操作完成的 WaitHandle

    —  CompletedSynchronously:获取异步操作是否同步完成的指示。

    —  IsCompleted:获取异步操作是否已完成的指示。

    使用BeginInvokeEndInvoke进行异步调用的常用方法主要有四种:

    调用BeginInvoke方法启动异步方法,进行某些操作,然后调用EndInvoke方法来一直阻止请求线程到调用完成。

    调用BeginInvoke方法启动异步方法,使用System.IAsyncResult.AsyncWaitHandle属性获取WaitHandle,使用它的WaitOne方法一直阻止执行直到发出WaitHandle信号,然后调用EndInvoke方法。

    调用BeginInvoke方法启动异步方法,轮询由BeginInvoke返回的IAsyncResult,确定异步调用何时完成,然后调用EndInvoke

    调用BeginInvoke方法启动异步方法时,将代表异步方法完成时需要回调的方法的委托传递给BeginInvoke。异步调用完成后,将在ThreadPool线程上执行该回调方法。在该回调方法中调用EndInvoke

    每种方法都是通过BeginInvoke方法来启动异步方法,调用EndInvoke方法来完成异步调用。

    1.直接调用EndInvoke 方法等待异步调用结束

    异步执行方法的最简单的方式是通过调用委托的BeginInvoke方法来开始执行方法,在主线程上执行一些工作,然后调用委托的EndInvoke方法。EndInvoke可能会阻止调用线程,因为它直到异步调用完成之后才返回。这种技术非常适合于文件或网络操作,但是由于EndInvoke会阻止它,所以不要从服务于用户界面的线程中调用它。

    下面的代码说明了如何使用这种方法来进行异步调用,并获得异步方法的结果:

    // AsynCall1.cs

    // 异步调用示例: 直接调用EndInvoke 方法等待异步调用结束

    using System;

    using System.Threading;

    // 定义异步调用方法的委托

    // 它的签名必须与要异步调用的方法一致

    public delegate int AsynComputeCaller(ulong l, out ulong factorial);

    public class Factorial

    {

        // 计算阶乘

        public ulong Compute(ulong l)

        {

            // 不要太快 :-)

            Thread.Sleep(50);

            if (l == 1)

            {

                return 1;

            }

            else

            {

                return l * Compute(l - 1);

            }

        }

        // 要异步调用的方法

        // 1. 调用Factorial方法来计算阶乘,并用out参数返回

        // 2. 统计计算阶乘所用的时间,并返回该值

        public int AsynCompute(ulong l, out ulong factorial)

        {

            Console.WriteLine("开始异步方法");

           

            DateTime startTime = DateTime.Now;

            factorial = Compute(l);

            TimeSpan usedTime =

                          new TimeSpan(DateTime.Now.Ticks - startTime.Ticks);

           

            Console.WriteLine("结束异步方法");

            return usedTime.Milliseconds;

        }

    }

    public class Test

    {

        public static void Main()

        {

            // 创建包含异步方法的类的实例

            Factorial fact = new Factorial();

            // 创建异步委托

            AsynComputeCaller caller = new AsynComputeCaller(fact.AsynCompute);

            Console.WriteLine("启动异步调用");

            ulong l = 30;

            ulong lf;

            // 启动异步调用

            IAsyncResult result = caller.BeginInvoke(l, out lf, null, null);

            // 主线程进行一些操作

            Thread.Sleep(0);

            Console.WriteLine("主线程进行一些操作");

            // 调用EndInvoke来等待异步调用结束,并获得结果

            int returnValue = caller.EndInvoke(out lf, result);

            // 异步调用的方法已经结束,显示结果

            Console.WriteLine("已经得到结果:{0}的阶乘为{1},计算时间为{2}毫秒",

                                   l, lf, returnValue);

        }

    }

    2.使用 WaitHandle 等待异步调用结束

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

    如果使用WaitHandle,则在异步调用完成之前或之后,在通过调用EndInvoke检索结果之前,还可以执行其他处理。

    下面的代码说明了如何使用这种方法来进行异步调用,并获得异步方法的结果:

    // AsynCall2.cs

    // 异步调用示例:使用 WaitHandle 等待异步调用结束

    using System;

    using System.Threading;

    // 定义异步调用方法的委托

    // 它的签名必须与要异步调用的方法一致

    public delegate int AsynComputeCaller(ulong l, out ulong factorial);

    public class Factorial

    {

        // 计算阶乘

        public ulong Compute(ulong l)

        {

            // 不要太快 :-)

            Thread.Sleep(50);

            if (l == 1)

            {

                return 1;

            }

            else

            {

                return l * Compute(l - 1);

            }

        }

        // 要异步调用的方法

        // 1. 调用Factorial方法来计算阶乘,并用out参数返回

        // 2. 统计计算阶乘所用的时间,并返回该值

        public int AsynCompute(ulong l, out ulong factorial)

        {

            Console.WriteLine("开始异步方法");

           

            DateTime startTime = DateTime.Now;

            factorial = Compute(l);

            TimeSpan usedTime =

                            new TimeSpan(DateTime.Now.Ticks - startTime.Ticks);

           

            Console.WriteLine("结束异步方法");

            return usedTime.Milliseconds;

        }

    }

    public class Test

    {

        public static void Main()

        {

            // 创建包含异步方法的类的实例

            Factorial fact = new Factorial();

            // 创建异步委托

            AsynComputeCaller caller = new AsynComputeCaller(fact.AsynCompute);

            Console.WriteLine("启动异步调用");

            ulong l = 30;

            ulong lf;

            // 启动异步调用

            IAsyncResult result = caller.BeginInvoke(l, out lf, null, null);

            // 主线程进行一些操作

            Thread.Sleep(0);

            Console.WriteLine("主线程进行一些操作");

            // 等待WaitHandle接收到信号

            Console.WriteLine("等待WaitHandle接收到信号");

            result.AsyncWaitHandle.WaitOne();

            // 主线程进行一些操作

            Thread.Sleep(0);

            Console.WriteLine("异步方法已经结束,主线程进行另外一些操作");

            // 调用EndInvoke来获得结果

            int returnValue = caller.EndInvoke(out lf, result);

            // 异步调用的方法已经结束,显示结果

            Console.WriteLine("{0}的阶乘为{1},计算时间为{2}毫秒",

                                  l, lf, returnValue);

        }

    }

    3.轮询异步调用是否完成

    可以使用由BeginInvoke方法返回的IAsyncResultIsCompleted属性来发现异步调用何时完成。从用户界面的服务线程中进行异步调用时可以执行此操作。轮询完成允许调用线程在异步调用在线程池线程上执行时继续执行。

    下面的代码说明了如何使用这种方法来进行异步调用,并获得异步方法的结果:

    // AsynCall3.cs

    // 异步调用示例: 轮询异步调用是否完成

    using System;

    using System.Threading;

    // 定义异步调用方法的委托

    // 它的签名必须与要异步调用的方法一致

    public delegate int AsynComputeCaller(ulong l, out ulong factorial);

    public class Factorial

    {

        // 计算阶乘

        public ulong Compute(ulong l)

        {

            // 不要太快 :-)

            Thread.Sleep(50);

            if (l == 1)

            {

                return 1;

            }

            else

            {

                return l * Compute(l - 1);

            }

        }

        // 要异步调用的方法

        // 1. 调用Factorial方法来计算阶乘,并用out参数返回

        // 2. 统计计算阶乘所用的时间,并返回该值

        public int AsynCompute(ulong l, out ulong factorial)

        {

            Console.WriteLine("开始异步方法");

           

            DateTime startTime = DateTime.Now;

            factorial = Compute(l);

            TimeSpan usedTime =

                                   new TimeSpan(DateTime.Now.Ticks - startTime.Ticks);

           

            Console.WriteLine("\n结束异步方法");

            return usedTime.Milliseconds;

        }

    }

    public class Test

    {

        public static void Main()

        {

            // 创建包含异步方法的类的实例

            Factorial fact = new Factorial();

            // 创建异步委托

            AsynComputeCaller caller = new AsynComputeCaller(fact.AsynCompute);

            Console.WriteLine("启动异步调用");

            ulong l = 30;

            ulong lf;

            // 启动异步调用

            IAsyncResult result = caller.BeginInvoke(l, out lf, null, null);

            // 轮询异步方法是否结束

            Console.WriteLine("主线程进行一些操作");

            while (result.IsCompleted == false)

            {

                // 主线程进行一些操作

                Thread.Sleep(10);

                Console.Write(".");

            }

            // 主线程进行一些操作

            Thread.Sleep(0);

            Console.WriteLine("异步方法已经结束,主线程进行另外一些操作");

            // 调用EndInvoke来获得结果

            int returnValue = caller.EndInvoke(out lf, result);

            // 异步调用的方法已经结束,显示结果

            Console.WriteLine("已经得到结果:{0}的阶乘为{1},计算时间为{2}毫秒",

                                   l, lf, returnValue);

        }

    }

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

    如果启动异步调用的线程是不需要处理结果的线程,则可以在调用完成时执行回调方法。回调方法在线程池线程上执行。

    若要使用回调方法,必须将引用回调方法的AsyncCallback委托传递给BeginInvoke。也可以传递包含回调方法将要使用的信息的对象。例如,可以传递启动调用时曾使用的委托,以便回调方法能够调用EndInvoke方法。

    下面的代码说明了如何使用这种方法来进行异步调用,并获得异步方法的结果:

    // AsynCall4.cs

    // 异步调用示例: 在异步调用完成时执行回调方法

    using System;

    using System.Threading;

    // 定义异步调用方法的委托

    // 它的签名必须与要异步调用的方法一致

    public delegate int AsynComputeCaller(ulong l, out ulong factorial);

    public class Factorial

    {

        // 计算阶乘

        public ulong Compute(ulong l)

        {

            // 不要太快 :-)

            Thread.Sleep(50);

            if (l == 1)

            {

                return 1;

            }

            else

            {

                return l * Compute(l - 1);

            }

        }

        // 要异步调用的方法

        // 1. 调用Factorial方法来计算阶乘,并用out参数返回

        // 2. 统计计算阶乘所用的时间,并返回该值

        public int AsynCompute(ulong l, out ulong factorial)

        {

            Console.WriteLine("开始异步方法");

           

            DateTime startTime = DateTime.Now;

            factorial = Compute(l);

            TimeSpan usedTime =

                                   new TimeSpan(DateTime.Now.Ticks - startTime.Ticks);

           

            Console.WriteLine("结束异步方法");

            return usedTime.Milliseconds;

        }

    }

    public class Test

    {

        static ulong l = 30;

        static ulong lf;

        public static void Main()

        {

            // 创建包含异步方法的类的实例

            Factorial fact = new Factorial();

            // 创建异步委托

            AsynComputeCaller caller = new AsynComputeCaller(fact.AsynCompute);

            // 启动异步调用

            Console.WriteLine("启动异步调用");

            IAsyncResult result = caller.BeginInvoke(l, out lf,

                                    new AsyncCallback(CallbackMethod), caller);

            // 主线程进行一些操作

            Thread.Sleep(0);

            Console.WriteLine("主线程进行一些操作");

            Console.WriteLine("Enter键结束程序...");

            Console.ReadLine();

        }

        // 在异步调用完成时执行的回调方法

        // 该回调方法的签名必须与AsyncCallback委托一致

        static void CallbackMethod(IAsyncResult ar)

        {

            // 获取委托

            AsynComputeCaller caller = (AsynComputeCaller)ar.AsyncState;

            // 调用EndInvoke来获得结果

            int returnValue = caller.EndInvoke(out lf, ar);

            // 异步调用的方法已经结束,显示结果

            Console.WriteLine("已经得到结果:{0}的阶乘为{1},计算时间为{2}毫秒",

                                   l, lf, returnValue);

        }

    }

  • 相关阅读:
    数组的Clone方法
    反射创建类的一种方法
    css比较容易搞混的三个选择器
    java8 引进lamda
    js动态创建的元素绑定事件
    【Alpha版本】项目测试
    第五次团队作业——【Alpha版本】随笔汇总
    【Alpha版本】项目总结
    【Alpha版本】冲刺阶段——Day 10
    【Alpha版本】冲刺阶段——Day 9
  • 原文地址:https://www.cnblogs.com/vincedotnet/p/881045.html
Copyright © 2011-2022 走看看