• C# 1.0 新特性之异步委托(AP、APM)


    Ø  前言

    C# 异步委托也是属于异步编程中的一种,可以称为 Asynchronous Programming(异步编程)或者 Asynchronous Programming Model(异步编程模型),因为这是实现异步编程的模式。委托是 C#1.0 就有的特性,并且 .NET v1.0 同时也伴随有 AsyncCallbackIAsyncResult 等类/接口的出现,所以所有的 .NET 版本中都是支持的。

     

    1.   什么是异步委托

    1)   异步委托是采用异步回调的方式实现异步执行,当使用委托异步执行某个方法时,将从线程池中取出一个线程去执行该方法。

    2)   当执行完成后则调用 AsyncCallback 委托指定的方法,完成异步回调。

    3)   开始执行一个异步委托后,可以使用4种方式等待异步执行完成:

    1.   开启异步委托后,BeginInvoke() 方法将返回一个实现了 IAsyncResult 接口的 System.Runtime.Remoting.Messaging.AsyncResult 对象。使用该对象的 AsyncWaitHandle 属性,并调用 WaitOne() 方法,该方法会阻塞当前线程,直到收到信号(异步委托方法执行完成)。

    2.   调用委托对象的 EndInvoke() 方法,需要传递一个 AsyncResult 对象,该方法也用于获取异步委托的返回值,所以这种方式也会阻塞当前线程。

    3.   使用 IAsyncResult.IsCompleted 属性,判断是否执行完成。该属性在异步委托方法执行完成时 true.

    4.   【推荐】使用异步回调委托的方式,当异步委托方法执行完成后调用,如果在不需要非要等到异步完成时获取返回结果的情况下,推荐使用该方式。

     

    2.   下面分别使用这四种方式等待

    首先,定义四个委托类型。

    public delegate void MyDelegate1();

    public delegate string MyDelegate2();

    public delegate void MyDelegate3(string str);

    public delegate int MyDelegate4(int num1, int num2);

     

    1)   使用 WaitOne() 方法(匿名方法)

    /// <summary>

    /// 使用 WaitOne() 方法(匿名方法)。

    /// </summary>

    public void AsyncDelegateTest1()

    {

        WriteLine("AsyncDelegateTest1() 方法开始执行,线程Id{0}", GetThreadId());

        MyDelegate1 d1 = new MyDelegate1(delegate()

        {

            WriteLine("匿名方法开始执行,线程Id{0}{1}", GetThreadId(), GetTime());

            Thread.Sleep(3000);

            WriteLine("匿名方法结束执行,线程Id{0}", GetThreadId());

        });

        IAsyncResult ar = d1.BeginInvoke(null, null);

        ar.AsyncWaitHandle.WaitOne();   //这里将阻塞线程,直到收到信号(异步方法执行完成)

        WriteLine("AsyncDelegateTest1() 方法结束执行,线程Id{0}{1}", GetThreadId(), GetTime());

    }

    运行以上代码:

    clip_image002[4]

     

    2)   使用委托对象的 EndInvoke() 方法(匿名方法)

    /// <summary>

    /// 使用委托对象的 EndInvoke() 方法(匿名方法)。

    /// </summary>

    public void AsyncDelegateTest2()

    {

        WriteLine("AsyncDelegateTest2() 方法开始执行,线程Id{0}", GetThreadId());

        MyDelegate2 d2 = new MyDelegate2(delegate()

        {

            WriteLine("匿名方法开始执行,线程Id{0}{1}", GetThreadId(), GetTime());

            Thread.Sleep(3000);

            WriteLine("匿名方法结束执行,线程Id{0}", GetThreadId());

            return System.Reflection.MethodBase.GetCurrentMethod().Name;

        });

        IAsyncResult ar = d2.BeginInvoke(null, null);

        string result = d2.EndInvoke(ar);   //这里也将阻塞线程,直到异步方法执行完成

        WriteLine("AsyncDelegateTest2() 方法结束执行,{0},异步委托返回结果:{1}", GetTime(), result);

    }

    运行以上代码:

    clip_image004[4]

     

    3)   使用 IAsyncResult.IsCompleted 属性(Lambda 表达式)

    /// <summary>

    /// 使用 IAsyncResult.IsCompleted 属性(Lambda 表达式)。

    /// </summary>

    public void AsyncDelegateTest3()

    {

        WriteLine("AsyncDelegateTest3() 方法开始执行,线程Id{0}", GetThreadId());

        MyDelegate3 d3 = new MyDelegate3((str) =>

        {

            WriteLine("Lambda 表达式开始执行,线程Id{0}{1}", GetThreadId(), GetTime());

            Thread.Sleep(3000);

            WriteLine("Lambda 表达式结束执行,str{0}", str);

        });

        IAsyncResult ar = d3.BeginInvoke("这是一段话!", null, null);

        while (!ar.IsCompleted) //标记是否完成(其实与直接调 EndInvoke() 方法没什么区别)

        { }

        WriteLine("AsyncDelegateTest3() 方法结束执行,线程Id{0},{1}", GetThreadId(), GetTime());

    }

    运行以上代码:

    clip_image005[4]

     

    4)   【推荐】使用异步回调委托

    /// <summary>

    /// 【推荐】使用异步回调委托。

    /// </summary>

    public void AsyncDelegateTest4()

    {

        WriteLine("AsyncDelegateTest4() 方法开始执行,线程Id{0}", GetThreadId());

        MyDelegate4 d4 = new MyDelegate4(Add);

        //这里必须将第二个参数(委托对象)传入,否则异步回调中 IAsyncResult.AsyncState 属性将为 null.

        IAsyncResult ar = d4.BeginInvoke(22, 36, new AsyncCallback(AddCallback), d4);

        WriteLine("AsyncDelegateTest4() 方法结束执行,线程Id{0}", GetThreadId());

    }

     

    public int Add(int num1, int num2)

    {

        WriteLine("Add() 方法开始执行,线程Id{0}{1}", GetThreadId(), GetTime());

        Thread.Sleep(3000);

        WriteLine("Add() 方法结束执行,线程Id{0}", GetThreadId());

        return num1 + num2;

    }

     

    public void AddCallback(IAsyncResult ar)

    {

        WriteLine("AddCallback() 方法开始执行,线程Id{0}{1}", GetThreadId(), GetTime());

        MyDelegate4 d4 = ar.AsyncState as MyDelegate4;  //获取委托对象

        int result = d4.EndInvoke(ar); //这里并不会阻塞

        WriteLine("AddCallback() 方法结束执行,计算结果:{0}{1}", result, GetTime());

    }

    运行以上代码:

    clip_image007[4]

     

    3.   下面,再来看下 C# 中一些常用基于异步回调的运用

    1)   模拟 Web 请求,异步读取响应流

    /// <summary>

    /// 异步获取网页 HTML 内容。

    /// </summary>

    public void AsyncGetHtmlString()

    {

        WriteLine("AsyncGetHtmlString() 方法开始执行,线程Id{0}{1}", GetThreadId(), GetTime());

        WebRequest request = WebRequest.Create("http://www.cnblogs.com/abeam/");

        request.BeginGetResponse(new AsyncCallback(ResponseCallback), request);

        WriteLine("AsyncGetHtmlString() 方法结束执行,线程Id{0}", GetThreadId());

    }

     

    public async void ResponseCallback(IAsyncResult ar)

    {

        WriteLine("ResponseCallback() 方法开始执行(此时已经获得响应),线程Id{0}{1}", GetThreadId(), GetTime());

        WebRequest request = ar.AsyncState as WebRequest;

        using (WebResponse response = request.EndGetResponse(ar))

        {

            using (var stream = response.GetResponseStream())

            {

                WriteLine("开始异步读取,线程Id{0}{1}", GetThreadId(), GetTime());

                WriteLine("响应的 HTML 内容:");

                int count, totalCount = 0;

     

                //1. 同步读取响应流

                using (var sr = new System.IO.StreamReader(stream, Encoding.UTF8))

                {

                    char[] chars = new char[256];

                    while ((count = sr.Read(chars, 0, chars.Length)) > 0)

                    {

                        totalCount += count;

                        if (totalCount <= chars.Length) //太多屏幕容不下

                        {

                            string content = new string(chars, 0, count);

                            WriteLine(content);

                            WriteLine("同步读取流线程Id{0}", GetThreadId());

                        }

                    }

                }

                WriteLine("响应的 HTML 总字符数:{0}", totalCount);

     

                //2. 异步读取响应流

                /*

                    * byte[] buffer = new byte[stream.Length];

                    * int totalCount = await stream.ReadAsync(buffer, 0, buffer.Length);

                    * 不能使用 stream.Length,因为 stream 是一种 System.Net.ConnectStream,否者将报异常:

                    * 未处理System.NotSupportedException

                    * Message: System.NotSupportedException”类型的未经处理的异常在 mscorlib.dll 中发生

                    * 其他信息: 此流不支持查找操作。

                    */

                byte[] buffer = new byte[1024];

                while ((count = await stream.ReadAsync(buffer, 0, buffer.Length)) > 0)

                {

                    totalCount += count;

                    if (totalCount <= 1024)  //太多屏幕容不下

                    {

                        string content = Encoding.UTF8.GetString(buffer);

                        Write(content);

                    }

                    WriteLine();

                    Write("异步读取流线程Id{0}", GetThreadId());

                }

                WriteLine();

                WriteLine("响应的 HTML 总字节数:{0}", totalCount);

            }

        }

    }

    下面是两种读取方式的结果:

    clip_image009[4]

    clip_image011[4]

     

    Ø  总结

    1.   异步委托主要使用 BeginInvoke() 方法开启异步委托,该方法传入一个回调委托 AsyncCallback 对象。

    2.   BeginInvoke() 返回一个实现了 IAsyncResult 接口的对象,可以使用该对象的 AsyncWaitHandle.WaitOne() 方法和 IsCompleted 属性判断异步是否完成。

    3.   同样 AsyncCallback 委托的签名也有个 IAsyncResult 参数,该委托将在异步调用完成时执行

    4.   需要获取异步委托的返回结果,都必须调用 EndInvoke() 方法。

  • 相关阅读:
    SqlLite的使用
    asp.net批量上传图片带进度条显示
    对于GridView控件的RowDataBount事件的错误理解
    关于SQL中时间对比
    关于使用触发器时使用@@identity的问题
    关于Treeview控件如何给每个节点加js脚本的方法
    /etc/init.d/functions详解
    如何解决安装DreamWeaver8 时候提示“无法将数值写入键/SOFTWARE/classes/.shtml”
    [请教]关于超大数据量网站的数据搜索和分页的实现方法
    svchost.exe[900]中发生未处理的win32异常
  • 原文地址:https://www.cnblogs.com/abeam/p/11227923.html
Copyright © 2020-2023  润新知