• 三种异步编程模型


    Net 中很多的类接口设计的时候都考虑了多线程问题,简化了多线程程序的开发。 不用自己去写 WaitHandler 等这些底层的代码。由于历史的发展,这些类的接口设计有着三种不同的风格: EAP(*)
    APM(*)TPL。目前重点用 TPL

    EAP


    EAP Event-based Asynchronous Pattern( 基于事件的异步模型) 的简写, 类似于 Ajax 中的XmlHttpRequestsend 之后并不是处理完成了,而是在 onreadystatechange 事件中再通知处理完成。

     public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }    
    
            private void button1_Click(object sender, EventArgs e)
            {
             
                WebClient client = new WebClient();
                client.DownloadStringCompleted += Client_DownloadStringCompleted;
                client.DownloadStringAsync(new Uri("http://www.baidu.com")); ;
    
            }
    
            private void Client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
            {
                MessageBox.Show(e.Result);
            }
        }

    优点是简单,缺点是当实现复杂的业务的时候很麻烦,比如下载 A 成功后再下载 b,如果下载 b成功再下载 c,否则就下载 d
    EAP 的类的特点是:一个异步方法配一个***Completed 事件。 .Net 中基于 EAP 的类比较少。也有更好的替代品,因此了解即可。

    AMP


    APM(Asynchronous Programming Model).Net 旧版本中广泛使用的异步编程模型。使用了 APM的异步方法会返回一个 IAsyncResult 对象, 这个对象有一个重要的属性 AsyncWaitHandle, 他是一个
    用来等待异步任务执行结束的一个同步信号。

     public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
    
            private void button1_Click(object sender, EventArgs e)
            {
                FileStream fs = File.OpenRead("d:/1.txt");
                byte[] buffer = new byte[16];
                IAsyncResult aResult = fs.BeginRead(buffer, 0, buffer.Length, null, null);
                aResult.AsyncWaitHandle.WaitOne();//等待任务执行结束
                MessageBox.Show(Encoding.UTF8.GetString(buffer));
                fs.EndRead(aResult);
            }
        }

    如果不加 aResult.AsyncWaitHandle.WaitOne() 那么很有可能打印出空白,因为 BeginRead只是“开始读取”。调用完成一般要调用 EndXXX 来回收资源。APM 的特点是:方法名字以 BeginXXX 开头,返回类型为 IAsyncResult, 调用结束后需要EndXXX
    .Net 中有如下的常用类支持 APMStreamSqlCommandSocket 等。
    APM 还是太复杂,了解即可。

    TPL


    TPLTask Parallel Library)是.Net 4.0 之后带来的新特性,更简洁,更方便。现在在.Net平台下已经大面积使用。

      private void button1_Click(object sender, EventArgs e)
            {
                FileStream fs = File.OpenRead("d:/1.txt");
                byte[] buffer = new byte[16];
                Task<int> task = fs.ReadAsync(buffer, 0, buffer.Length);
                task.Wait();
                MessageBox.Show("读取了" + task.Result + "个字节");
                MessageBox.Show(Encoding.UTF8.GetString(buffer));
            }

    使用async和awit关键字

     private async void button2_Click(object sender, EventArgs e)
            {
                FileStream fs = File.OpenRead("d:/1.txt");
                byte[] buffer = new byte[16];
                int len = await fs.ReadAsync(buffer, 0, buffer.Length);
                MessageBox.Show("读取了" + len + "个字节");
                MessageBox.Show(Encoding.UTF8.GetString(buffer));
            }

    注意方法中如果有 await,则方法必须标记为 async,不是所有方法都可以被轻松的标记为 asyncWinForm 中的事件处理方法都可以标记为 asyncMVC 中的 Action 方法也可以标记为 async、控制台的 Main 方法不能标记为 async
    TPL 的特点是:方法都以 XXXAsync 结尾,返回值类型是泛型的 Task<T>
    TPL 让我们可以用线性的方式去编写异步程序,不再需要像 EAP 中那样搞一堆回调、逻辑跳来跳去了。 await 现在已经被 JavaScript 借鉴走了!

     
    await 实现“先下载 A,如果下载的内容长度大于 100 则下载 B,否则下载 C”就很容易了
    再看看 WebClient TPL 用法:

    private async void button3_Click(object sender, EventArgs e)
            {
                var wc = new WebClient();
                string html = await wc.DownloadStringTaskAsync("http://www.baidu.com");//不要丢了 await
                MessageBox.Show(html);
                //上面的代码并不是完全等价于
                WebClient wc1 = new WebClient();
                var task = wc1.DownloadStringTaskAsync("http://www.baidu.com");
                task.Wait();
                MessageBox.Show(task.Result);
    
                //  因为如果按照上面的写法,会卡死 UI 线程
                //而 await 则 不 会 。。。 好 像 不 是 ? ? ? 那 只 是 因 为 把 html 这 么 长 的 字 符 串
                //MessageBox.Show 很慢, MessageBox.Show(html.Substring(10)); 就证明了这一点
    
    
                // Task<T> 中的 T 是什么类型每个方法都不一样,要看文档。
                //WebClient、 Stream、 Socket 等这些“历史悠久”的类都同时提供了 APM、 TPL 风格的
                //API,甚至有的还提供了 EAP 风格的 API。尽可能使用 TPL 风格的
            }

    编写异步方法

    返回值为 Task<T>,潜规则(不要求)是方法名字以 Async 结尾:

     static Task<string> F2Async()
            {
                return Task.Run(() =>
                {
                    System.Threading.Thread.Sleep(2000);
                    return "F2";
                });
            }

    调用:

     private async void button4_Click(object sender, EventArgs e)
            {
                string s = await F2Async();
                MessageBox.Show(s);
    
            }

    TPL写法


     Task.Run()一个用来把一个代码段包装为 Task<T>的方法 Run 中委托的代码体就是异步任务执行的逻辑,最后 return 返回值。

    public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
    
            private async void button1_Click(object sender, EventArgs e)
            {
                /*  string i1 = await F1Async();
                  MessageBox.Show("i1=" + i1);
                  string i2 = await F2Async();
                  MessageBox.Show("i2=" + i2);
                  */
    
                //Task.Run()一个用来把一个代码段包装为 Task<T>的方法 Run 中委托的代码体就是异步任
                //务执行的逻辑,最后 return 返回值。
                //把 button1_click 改成:
                Task<string> task1 = F1Async();
                Task<string> task2 = F2Async();
                string i1 = await task1;
                MessageBox.Show("i1=" + i1);
                string i2 = await task2;
                MessageBox.Show("i2=" + i2);
    
            }
            static Task<string> F1Async()
            {
                MessageBox.Show("F1 Start");
                return Task.Run(() =>
                {
                    System.Threading.Thread.Sleep(1000);
                    MessageBox.Show("F1 Run");
                    return "F1";
                });
            }
            static Task<string> F2Async()
            {
                MessageBox.Show("F2 Start");
                return Task.Run(() =>
                {
                    System.Threading.Thread.Sleep(2000);
                    MessageBox.Show("F2 Run");
                    return "F2";
                });
            }
       
        }

    使用async返回值和Task.Run()返回值

     static Task<string> GetRuPengAsync()
            {
                return Task.Run(() =>
                {
                    return "a";
                });
                //这里可以return Task<string> 类型的值,不能return “a”;
            }
            static async Task<string> GetRuPengAsync1()
            {
                return  "a";
                //这里不可以return Task<string> 类型的值, 而可以能return “a”;
            }

    1、 只要方法是 Task<T>类型的返回值,都可以用 await 来等待调用获取返回值
    2、 如果一个返回 Task<T>类型的方法被标记了 async,那么只要方法内部直接 return T 这个类型的实例就可以。
    3、 一个返回 Task<T>类型的方法没有被标记了 async,那么需要方法内部直接 Task 实例

       private async void button2_Click(object sender, EventArgs e)
            {
                //int i = await F1Async();
                int i = await M2Async();
                MessageBox.Show(i.ToString());
            }
    
            static Task<int> M1Async()
            {
                return Task.Run(() => {
                    return 2;
                });
            }
            static async Task<int> M2Async()
            {
                return 2;
            }

     

    TPL高级用法


    1、 如果方法内部有 await,则方法必须标记为 asyncasp.net mvc ActionWinForm 的事件处理函数都可以标记 async,控制台 Main 不能 async。对于不能标记为怎么办?
      F1Async().Result 注意有的上下文下会有死锁。
    2、 如果返回值就是一个立即可以随手可得的值,那么就用 Task.FromResult()如果是一个需要休息一会的任务(比如下载失败则过 5 秒钟后重试。主线程不休息,和Thread.Sleep 不一样),

      那么就用 Task.Delay()
    3Task.Factory.FromAsync()IAsyncResult 转换为 Task, 这样 APM 风格的 api 也可以用 await 来调用。
    4、 编写异步方法的简化写法。如果方法声明为 async,那么可以直接 return 具体的值,不再用创建Task,由编译器创建 Task

            static async Task<int> F1Async()
            {
                return 1;
            }
    
            //2、 如果返回值就是一个立即可以随手可得的值,那么就用 Task.FromResult()
            static Task<int> F2Async()
            {
                return Task.FromResult(3);
            }
    static Task<int> F3Async() { return Task.Run(() => { return 1 + 3; }); }

    WinForm 程序依次下载三个网址: Task.WaitAll(task1, task2, task3);Task.WaitAll 是等待所有任务完成:

    private async void button2_Click(object sender, EventArgs e)
            {
                /* HttpClient wc = new HttpClient();
                 string s1 = await wc.GetStringAsync(textBox1.Text);
                 label1.Text = s1.Length.ToString();
                 string s2 = await wc.GetStringAsync(textBox2.Text);
                 label2.Text = s2.Length.ToString();
                 string s3 = await wc.GetStringAsync(textBox3.Text);
                 label3.Text = s3.Length.ToString(); */
    
    
                HttpClient hc = new HttpClient();
                var task1 = hc.GetStringAsync(textBox1.Text);
                var task2 = hc.GetStringAsync(textBox2.Text);
                var task3 = hc.GetStringAsync(textBox3.Text);
                Task.WaitAll(task1, task2, task3);
                label1.Text = task1.Result.Length.ToString();
                label2.Text = task2.Result.Length.ToString();
                label3.Text = task3.Result.Length.ToString();
    
            }

     

    TPL异常处理


    1TPL 中,如果程序中出现异常,除非进行 try...catch,否则有可能是感觉不到出了异常的。测试,把上面下载程序的域名改成一个不存在的域名。
    2TPL 程序有时候还会抛出 AggregateException, 这通常发生在并行有多个任务执行的情况下。 比如:

    private async void button3_Click(object sender, EventArgs e)
            {
                try
                {
                    HttpClient hc = new HttpClient();
                    var task1 = hc.GetStringAsync(textBox1.Text);
                    var task2 = hc.GetStringAsync(textBox2.Text);
                    var task3 = hc.GetStringAsync(textBox3.Text);
                    Task.WaitAll(task1, task2, task3);
                    label1.Text = task1.Result.Length.ToString();
                    label2.Text = task2.Result.Length.ToString();
                    label3.Text = task3.Result.Length.ToString();
                }
                catch (AggregateException ae)
                {
                    MessageBox.Show(ae.GetBaseException().ToString());
                }
            }

    因为多个并行的任务可能有多个有异常,因此会包装为 AggregateException 异常,AggregateException InnerExceptions 属性可以获得多个异常对象信息

  • 相关阅读:
    http statusCode(状态码) 200、300、400、500序列
    一行css代码调试中学到的javascript知识,很有意思
    jquery中的cookie
    图片预加载和AJAX的图片预加载
    关于图片懒加载
    iframe的自适应
    阻止微信端浏览器下滑出现空白条现象
    动态加载js文件
    kaldi 的安装和测试
    Python算法-排序
  • 原文地址:https://www.cnblogs.com/Jenkin/p/12048748.html
Copyright © 2020-2023  润新知