异步调用 (转载)

 

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

.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);

    }

}

原文地址:https://www.cnblogs.com/vincedotnet/p/881045.html