异步编程-模拟异步调用

版权声明:转载请注明出处。 https://blog.csdn.net/baidu_38304645/article/details/84677439

传统应用程序在调用一个方法时,需要等待该方法执行完成并返回调用处,然后再继续执行调用处后面的语句,但如果调用的方法本身需要执行较长的时间,则程序将长时间的等待。如果希望在调用一个方法时,能在该方法没有执行完成的时候继续执行其他代码,则需要异步编程。

异步编程的基本思想是,向其他组件发出方法调用,继续执行其他任务,而不用等待调用的操作完成

在多线程编程中,每个线程同时执行各自的任务,开发者必须在应用程序中创建并管理这些线程。异步编程也可以达到多线程效果,不同的是异步编程不需要创建多个线程,只需在主线程中发出一个异步调用,而不需要等待异步调用返回即可继续执行其他操作。如果需要返回异步调用结果,则需通过回调,轮询等方式来获得。

所有的异步调用都是由主线程发起,且独立于主线程之外单独执行,这样不但达到了多线程的效果,而且还避免了多线程的同步问题。因此,使用异步编程来执行多个任务要更简便些。

异步编程一般有两个逻辑部分:客户端调用开始方法并提供参数,从而启动异步操作;客户端通过调用结束方法,来获取异步操作的结果。

1、开始异步操作

调用方在调用开始方法时,除了提供必要的参数外,还可以提供一个可选的AsyncCallback委托,用来设置回调函数。开始方法会同步返回一个实现IAsyncResult接口的对象,调用方可以使用该接口的属性和方法来确定异步操作的状态或结果。

2、获取异步操作的结果

当操作完成时,调用者可以通过许多方法来获取操作结果。

首先是回调函数,如果提供了可选的AsyncCallback委托,那么当操作完成时,自动引用并执行该回调函数。

对于没有显示实现异步调用的方法,开发人员可以使用委托技术来获取操作结果。委托不但提供了对方法的封装,还提供了对方法进行异步调用的接口。编译器为每个委托类生成BeginInvoke和EndInvoke方法,用来实现异步调用。如果调用BeginInvoke()方法,则运行环境将对请求进行排队,并立即返回到调用方,来自线程池的某个线程将调用该方法,提交请求的原始线程将继续执行。

如果在BeginInvoke()方法中指定了回调方法,则当异步方法返回时,将调用该方法,而在回调中,使用EndInvoke()方法来获取返回值和输出参数。

运行结果:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Runtime.Remoting.Contexts;

namespace Test1_1
{
    class HelloWorld
    {
        public delegate string AsyncDelegate(int callTime);               //定义一个委托,用来提供异步调用的接口
        static void Main(string[] args)
        {
            AsyncDemo ad = new AsyncDemo();
            AsyncDelegate dlgy = new AsyncDelegate(ad.text);              //封装要异步调用的方法
            //开始异步调用(指定该方法的执行时间3秒),并指定回调方法
            IAsyncResult ar = dlgy.BeginInvoke(3000, new AsyncCallback(CallbackMethod), dlgy);
            //程序继续执行
            Console.WriteLine("主线程继续工作");               
            //模拟主程序需要执行的时间(1秒)
            Thread.Sleep(1000);
            Console.WriteLine("主线程工作完成,执行了1秒,等待异步调用完成");

            Console.Read();
        }
        //异步调用结束后的回调方法
        static void CallbackMethod(IAsyncResult ar)
        {
            //从异步操作的状态中提取AsyncDelegate委托
            AsyncDelegate dlgy = (AsyncDelegate)ar.AsyncState; 
            //获取异步调用的结果
            string result = dlgy.EndInvoke(ar);
            Console.WriteLine("异步调用完成,{0}", result);
        }
    }

    public class AsyncDemo
    {
        //异步调用的测试方法
        public string text(int callTime)
        {
            Console.WriteLine("异步调用的方法开始");
            //模拟该方法需要执行的时间(3秒)
            Thread.Sleep(callTime);

            return "方法需要时间" + (callTime / 1000) + "秒";
        }
    }
}

回调方法为CallbackMethod(),该方法返回值为void,并有一个IAsyncResult类型的参数。System命名空间定义了一个AsyncCallback委托类,该类与此方法签名匹配,因此无需声明新的委托类型。

该主线程调用test()方法后,并没有待方法返回,而是继续向下执行,同时,异步调用的方法也开始了执行。由于主线程只需执行1秒而子线程需执行3秒,所以主线程完成,两秒后异步调用结束。

如果没有BeginInvoke()方法中指定回调,则可以在提交请求的程序中使用其他异步设计模式技术。

轮询:调用方可以轮询检查IAsyncResult接口的IsCompleted属性,来确定调用是否完成。

运行结果:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Runtime.Remoting.Contexts;

namespace Test1_1
{
    class HelloWorld
    {
        public delegate string AsyncDelegate(int callTime);               //定义一个委托,用来提供异步调用的接口
        static void Main(string[] args)
        {
            AsyncDemo ad = new AsyncDemo();
            AsyncDelegate dlgy = new AsyncDelegate(ad.text);              //封装要异步调用的方法
            //开始异步调用(指定该方法的执行时间3秒),并指定回调方法
            IAsyncResult ar = dlgy.BeginInvoke(3000, null, null);

            //程序继续执行
            Console.WriteLine("主线程继续工作");               
            //模拟主程序需要执行的时间(1秒)
            Thread.Sleep(1000);
            Console.WriteLine("主线程工作完成,执行了1秒,等待异步调用完成");
            //轮询异步调用是否完成
            while (!ar.IsCompleted)
            {
                //模拟主程序下次轮询的时间(1秒)
                Thread.Sleep(1000);
                Console.WriteLine("主线程工作完成,等待异步调用完成");
            }
            //调用结束方法,获取调用结果
            string result = dlgy.EndInvoke(ar);
            Console.WriteLine("异步调用完成,{0}", result);

            Console.Read();
        }
    }

    public class AsyncDemo
    {
        //异步调用的测试方法
        public string text(int callTime)
        {
            Console.WriteLine("异步调用的方法开始");
            //模拟该方法需要执行的时间(3秒)
            Thread.Sleep(callTime);

            return "方法需要时间" + (callTime / 1000) + "秒";
        }
    }
}

主线程调用test()方法后,继续向下执行,同时,异步调用的方法也开始执行,由于主线程只需执行1秒,执行结束后,开始每隔1秒轮询异步调用是否完成,而异步调用需执行3秒,所以可以在两次轮询后异步调用结束。

猜你喜欢

转载自blog.csdn.net/baidu_38304645/article/details/84677439