C# Winform异步调用详解

 C#异步调用四大方法是什么呢?C#异步调用四大方法的使用是如何进行的呢?让我们首先了解下什么时候用到C#异步调用:

.NET Framework 允许您C#异步调用任何方法。定义与您需要调用的方法具有相同签名的委托;公共语言运行库将自动为该委托定义具有适当签名的 BeginInvoke 和 EndInvoke 方法。

BeginInvoke 方法用于启动C#异步调用。它与您需要异步执行的方法具有相同的参数,只不过还有两个额外的参数(将在稍后描述)。BeginInvoke 立即返回,不等待C#异步调用完成。BeginInvoke 返回 IasyncResult,可用于监视调用进度。

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

注意:Visual Studio .NET 中的智能感知功能会显示 BeginInvoke 和 EndInvoke 的参数。如果您没有使用 Visual Studio 或类似的工具,或者您使用的是 C# 和 Visual Studio .NET,请参见异步方法签名获取有关运行库为这些方法定义的参数的描述。
 一.C#异步调用四大方法详解

本主题中的代码演示了四种使用 BeginInvoke 和 EndInvoke 进行C#异步调用的常用方法。调用了 BeginInvoke 后,可以:

· 进行某些操作,然后调用 EndInvoke 一直阻塞到调用完成。

· 使用 IAsyncResult.AsyncWaitHandle 获取 WaitHandle,使用它的 WaitOne 方法将执行一直阻塞到发出 WaitHandle 信号,然后调用 EndInvoke。

· 轮询由 BeginInvoke 返回的 IAsyncResult,确定C#异步调用何时完成,然后调用 EndInvoke。

· 将用于回调方法的委托传递给 BeginInvoke。该方法在C#异步调用完成后在 ThreadPool 线程上执行,它可以调用 EndInvoke。

警告:始终在C#异步调用完成后调用 EndInvoke。

测试方法和异步委托

四个示例全部使用同一个长期运行的测试方法 TestMethod。该方法显示一个表明它已开始处理的控制台信息,休眠几秒钟,然后结束。TestMethod 有一个 out 参数(在 Visual Basic 中为 ByRef),它演示了如何将这些参数添加到 BeginInvoke 和 EndInvoke 的签名中。您可以用类似的方式处理 ref 参数(在 Visual Basic 中为 ByRef)。

下面的代码示例显示 TestMethod 以及代表 TestMethod 的委托;若要使用任一示例,请将示例代码追加到这段代码中。

注意   为了简化这些示例,TestMethod 在独立于 Main() 的类中声明。或者,TestMethod 可以是包含 Main() 的同一类中的 static 方法。

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

// you want to call asynchronously.  

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

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

}  

}  

    1:C#异步调用四大方法之使用 EndInvoke 等待异步调用

    异步执行方法的最简单方式是以 BeginInvoke 开始,对主线程执行一些操作,然后调用 EndInvoke。EndInvoke 直到C#异步调用完成后才返回。这种技术非常适合文件或网络操作,但是由于它阻塞 EndInvoke,所以不要从用户界面的服务线程中使用它。

    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, nullnull);  

    Thread.Sleep(0);  

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

    AppDomain.GetCurrentThreadId());  

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

    }  

      2:C#异步调用四大方法之使用 WaitHandle 等待异步调用

      等待 WaitHandle 是一项常用的线程同步技术。您可以使用由 BeginInvoke 返回的 IAsyncResult 的 AsyncWaitHandle 属性来获取 WaitHandle。C#异步调用完成时会发出 WaitHandle 信号,而您可以通过调用它的 WaitOne 等待它。

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

      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, nullnull);  

      Thread.Sleep(0);  

      Console.WriteLine("Main thread {0} does some work.",AppDomain.GetCurrentThreadId());  

      // Wait for the WaitHandle to become signaled.  

      ar.AsyncWaitHandle.WaitOne();  

      // Perform additional processing here.  

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

      }  

        3:C#异步调用四大方法之轮询异步调用完成

        您可以使用由 BeginInvoke 返回的 IAsyncResult 的 IsCompleted 属性来发现C#异步调用何时完成。从用户界面的服务线程中进行C#异步调用时可以执行此操作。轮询完成允许用户界面线程继续处理用户输入。

        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, nullnull);  

        // Poll while simulating work.  

        while(ar.IsCompleted == false)

         {  

        Thread.Sleep(10);  

        }  

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

        }  

          4:C#异步调用四大方法之异步调用完成时执行回调方法

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

          要使用回调方法,必须将代表该方法的 AsyncCallback 委托传递给 BeginInvoke。也可以传递包含回调方法将要使用的信息的对象。例如,可以传递启动调用时曾使用的委托,以便回调方法能够调用 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 reDIVsenting the callback method, and the data  

          // needed to call EndInvoke.  

          IAsyncResult ar dlgt.BeginInvoke(3000,  

          out threadId,   

          new AsyncCallback(CallbackMethod),dlgt );  

          Console.WriteLine("DIVss 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);  

          }  

           

             

            二.C#异步调用四大方法的基本内容就向你介绍到这里,下面看有回调函数的WebRequest和WebResponse的异步操作。

            using System;
            using System.Net;
            using System.Threading;
            using System.Text;
            using System.IO;


            // RequestState 类用于通过
            // 异步调用传递数据
            public class RequestState
            {
                const int BUFFER_SIZE 1024;
                public StringBuilder RequestData;
                public byte[] BufferRead;
                public HttpWebRequest Request;
                public Stream ResponseStream;
                // 创建适当编码类型的解码器
                public Decoder StreamDecode Encoding.UTF8.GetDecoder();

                public RequestState()
                {
                    BufferRead new byte[BUFFER_SIZE];
                    RequestData new StringBuilder("");
                    Request null;
                    ResponseStream null;
                }
            }

            // ClientGetAsync 发出异步请求
            class ClientGetAsync
            {
                public static ManualResetEvent allDone new ManualResetEvent(false);
                const int BUFFER_SIZE 1024;

                public static void Main(string[] args)
                {

                    if (args.Length 1)
                    {
                        showusage();
                        return;
                    }

                    // 从命令行获取 URI
                    Uri HttpSite new Uri(args[0]);

                    // 创建请求对象
                    HttpWebRequest wreq (HttpWebRequest)WebRequest.Create(HttpSite);

                    // 创建状态对象
                    RequestState rs new RequestState();

                    // 将请求添加到状态,以便它可以被来回传递
                    rs.Request wreq;

                    // 发出异步请求
                    IAsyncResult (IAsyncResult)wreq.BeginGetResponse(new AsyncCallback(RespCallback), rs);

                    // 将 ManualResetEvent 设置为 Wait,
                    // 以便在调用回调前,应用程序不退出
                    allDone.WaitOne();
                }

                public static void showusage()
                {
                    Console.WriteLine("尝试获取 (GET) 一个 URL");
                    Console.WriteLine("\r\n用法::");
                    Console.WriteLine("ClientGetAsync URL");
                    Console.WriteLine("示例::");
                    Console.WriteLine("ClientGetAsync http://www.microsoft.com/net/");
                }

                private static void RespCallback(IAsyncResult ar)
                {
                    // 从异步结果获取 RequestState 对象
                    RequestState rs (RequestState)ar.AsyncState;

                    // 从 RequestState 获取 HttpWebRequest
                    HttpWebRequest req rs.Request;

                    // 调用 EndGetResponse 生成 HttpWebResponse 对象
                    // 该对象来自上面发出的请求
                    HttpWebResponse resp (HttpWebResponse)req.EndGetResponse(ar);

                    // 既然我们拥有了响应,就该从
                    // 响应流开始读取数据了
                    Stream ResponseStream resp.GetResponseStream();

                    // 该读取操作也使用异步完成,所以我们
                    // 将要以 RequestState 存储流
                    rs.ResponseStream ResponseStream;

                    // 请注意,rs.BufferRead 被传入到 BeginRead。
                    // 这是数据将被读入的位置。
                    IAsyncResult iarRead ResponseStream.BeginRead(rs.BufferRead, 0, BUFFER_SIZE, new AsyncCallback(ReadCallBack), rs);
                }


                private static void ReadCallBack(IAsyncResult asyncResult)
                {
                    // 从 asyncresult 获取 RequestState 对象
                    RequestState rs (RequestState)asyncResult.AsyncState;

                    // 取出在 RespCallback 中设置的 ResponseStream
                    Stream responseStream rs.ResponseStream;

                    // 此时 rs.BufferRead 中应该有一些数据。
                    // 读取操作将告诉我们那里是否有数据
                    int read responseStream.EndRead(asyncResult);

                    if (read 0)
                    {
                        // 准备 Char 数组缓冲区,用于向 Unicode 转换
                        Char[] charBuffer new Char[BUFFER_SIZE];

                        // 将字节流转换为 Char 数组,然后转换为字符串
                        // len 显示多少字符被转换为 Unicode
                        int len rs.StreamDecode.GetChars(rs.BufferRead, 0, read, charBuffer, 0);
                        String str new String(charBuffer, 0, len);

                        // 将最近读取的数据追加到 RequestData stringbuilder 对象中,
                        // 该对象包含在 RequestState 
                        rs.RequestData.Append(str);


                        // 现在发出另一个异步调用,读取更多的数据
                        // 请注意,将不断调用此过程,直到
                        // responseStream.EndRead 返回 -1
                        IAsyncResult ar responseStream.BeginRead(rs.BufferRead, 0, BUFFER_SIZE, new AsyncCallback(ReadCallBack), rs);
                    }
                    else
                    {
                        if (rs.RequestData.Length 1)
                        {
                            // 所有数据都已被读取,因此将其显示到控制台
                            string strContent;
                            strContent rs.RequestData.ToString();
                            Console.WriteLine(strContent);
                        }

                        // 关闭响应流
                        responseStream.Close();

                        // 设置 ManualResetEvent,以便主线程可以退出
                        allDone.Set();
                    }
                    return;
                }
            }

            在这里有回调函数,且异步回调中又有异步操作。

            首先是异步获得ResponseStream,然后异步读取数据。

            这个程序非常经典。从中可以学到很多东西的。我们来共同探讨。

            总结

            上面说过,.net framework 可以异步调用任何方法。所以异步用处广泛。

            在.net framework 类库中也有很多异步调用的方法。一般都是已Begin开头End结尾构成一对,异步委托方法,外加两个回调函数和AsyncState参数,组成异步操作的宏观体现。所以要做异步编程,不要忘了委托delegate、Begin,End,AsyncCallBack委托,AsyncState实例(在回调函数中通过IAsyncResult.AsyncState来强制转换),IAsycResult(监控异步),就足以理解异步真谛了

            评论
            添加红包

            请填写红包祝福语或标题

            红包个数最小为10个

            红包金额最低5元

            当前余额3.43前往充值 >
            需支付:10.00
            成就一亿技术人!
            领取后你会自动成为博主和红包主的粉丝 规则
            hope_wisdom
            发出的红包
            实付
            使用余额支付
            点击重新获取
            扫码支付
            钱包余额 0

            抵扣说明:

            1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
            2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

            余额充值