zoukankan      html  css  js  c++  java
  • 并行开发 —— 第六篇 异步编程模型

         

          在.net里面异步编程模型由来已久,相信大家也知道Begin/End异步模式和事件异步模式,在task出现以后,这些东西都可以被task包装

    起来,可能有人会问,这样做有什么好处,下面一一道来。

    一: Begin/End模式

    1: 委托

        在执行委托方法的时候,我们常常会看到一个Invoke,同时也有一对你或许不常使用的BeginInvoke,EndInvoke方法对,当然Invoke方法

    是阻塞主线程,而BeginInvoke则是另开一个线程。

     1     class Program
     2     {
     3         static void Main(string[] args)
     4         {
     5             var func = new Func<string, string>(i => { return i + "i can fly"; });
     6 
     7             var state = func.BeginInvoke("yes,", Callback, func);
     8 
     9             Console.Read();
    10         }
    11 
    12         static void Callback(IAsyncResult async)
    13         {
    14             var result = async.AsyncState as Func<string, string>;
    15 
    16             Console.WriteLine(result.EndInvoke(async));
    17         }
    18     }

    下面我们用task包装一下

     1     class Program
     2     {
     3         static void Main(string[] args)
     4         {
     5             var func = new Func<string, string>(i =>
     6             {
     7                 return i + "i can fly";
     8             });
     9 
    10             Task<string>.Factory.FromAsync(func.BeginInvoke, func.EndInvoke, "yes,", null).ContinueWith
    11                 (i =>
    12                 {
    13                     Console.WriteLine(i.Result);
    14                 });
    15 
    16             Console.Read();
    17         }
    18     }

    可以看出,task只要一句就搞定,体现了task的第一个优点:简洁。

    2:流

        我们发现在Stream抽象类中提供了这样两对BeginRead/EndRead,BeginWrite/EndWrite(异步读写)的方法,这样它的n多继承类都可以

    实现异步读写,下面举个继承类FileStream的例子。

     1  static void Main(string[] args)
     2         {
     3             var path = "C://1.txt";
     4 
     5             FileStream fs = new FileStream(path, FileMode.Open);
     6 
     7             FileInfo info = new FileInfo(path);
     8 
     9             byte[] b = new byte[info.Length];
    10 
    11             var asycState = fs.BeginRead(b, 0, b.Length, (result) =>
    12             {
    13                 var file = result.AsyncState as FileStream;
    14 
    15                 Console.WriteLine("文件内容:{0}", Encoding.Default.GetString(b));
    16 
    17                 file.Close();
    18 
    19             }, fs);
    20 
    21             Console.WriteLine("我是主线程,我不会被阻塞!");
    22 
    23             Console.Read();
    24         }

    我们用task包装一下

     1    static void Main(string[] args)
     2         {
     3             var path = "C://1.txt";
     4 
     5             FileStream fs = new FileStream(path, FileMode.Open);
     6 
     7             FileInfo info = new FileInfo(path);
     8 
     9             byte[] b = new byte[info.Length];
    10 
    11             Task<int>.Factory.FromAsync(fs.BeginRead, fs.EndRead, b, 0, b.Length, null, TaskCreationOptions.None)
    12                 .ContinueWith
    13                 (i =>
    14                 {
    15                     Console.WriteLine("文件内容:{0}", Encoding.Default.GetString(b));
    16                 });
    17 
    18             Console.WriteLine("我是主线程,我不会被阻塞!");
    19 
    20             Console.Read();
    21         }

    其实看到这里,我们并没有发现task还有其他的什么优点,但是深入的想一下其实并不是这么回事,task能够游刃于线程并发和同步,而原始的异步

    编程要实现线程同步还是比较麻烦的。

         假如现在有这样的一个需求,我们需要从3个txt文件中读取字符,然后进行倒序,前提是不能阻塞主线程。如果不用task的话我可能会用工作线程

    去监视一个bool变量来判断文件是否全部读取完毕,然后再进行倒序,我也说了,相对task来说还是比较麻烦的,这里我就用task来实现。

     1     class Program
     2     {
     3         static byte[] b;
     4 
     5         static void Main()
     6         {
     7             string[] array = { "C://1.txt", "C://2.txt", "C://3.txt" };
     8 
     9             List<Task<string>> taskList = new List<Task<string>>(3);
    10 
    11             foreach (var item in array)
    12             {
    13                 taskList.Add(ReadAsyc(item));
    14             }
    15 
    16             Task.Factory.ContinueWhenAll(taskList.ToArray(), i =>
    17             {
    18                 string result = string.Empty;
    19 
    20                 //获取各个task返回的结果
    21                 foreach (var item in i)
    22                 {
    23                     result += item.Result;
    24                 }
    25 
    26                 //倒序
    27                 String content = new String(result.OrderByDescending(j => j).ToArray());
    28 
    29                 Console.WriteLine("倒序结果:"+content);
    30             });
    31 
    32             Console.WriteLine("我是主线程,我不会被阻塞");
    33 
    34             Console.ReadKey();
    35         }
    36 
    37         //异步读取
    38         static Task<string> ReadAsyc(string path)
    39         {
    40             FileInfo info = new FileInfo(path);
    41 
    42             byte[] b = new byte[info.Length];
    43 
    44             FileStream fs = new FileStream(path, FileMode.Open);
    45 
    46             Task<int> task = Task<int>.Factory.FromAsync(fs.BeginRead, fs.EndRead, b, 0, b.Length, null, TaskCreationOptions.None);
    47 
    48             //返回当前task的执行结果
    49             return task.ContinueWith(i =>
    50             {
    51                 return i.Result > 0 ? Encoding.Default.GetString(b) : string.Empty;
    52             }, TaskContinuationOptions.ExecuteSynchronously);
    53         }
    54     }

    可以看出,task的第二个优点就是:灵活性。

    这里可能就有人要问了,能不能用开多个线程用read以同步的形式读取,变相的实现文件异步读取,或许我们可能常听说程序优化后,最后出现的

    瓶颈在IO上面,是的,IO是比较耗费资源的,要命的是如果我们开的是工作线程走IO读取文件,那么该线程就会一直处于等待状态,不会再接收任

    何的外来请求,直到线程读取到文件为止,那么我们能不能用更少的线程来应对更多的IO操作呢?答案肯定是可以的,这里就设计到了”异步IO“的

    概念,具体内容可以参照百科:http://baike.baidu.com/view/1865389.htm  ,有幸的是beginXXX,endXXX完美的封装了“异步IO”。

    二:事件模式

       这个模式常以XXXCompleted的形式结尾,我们在文件下载这一块会经常遇到,这里我也举个例子。

     1     class Program
     2     {
     3         static void Main(string[] args)
     4         {
     5             WebClient client = new WebClient();
     6 
     7             client.DownloadFileCompleted += new System.ComponentModel.AsyncCompletedEventHandler(client_DownloadFileCompleted);
     8 
     9             client.DownloadFileAsync(new Uri("http://imgsrc.baidu.com/baike/abpic/item/6a600c338744ebf844a0bc74d9f9d72a6159a7ac.jpg"),
    10                                    "1.jpg", "图片下完了,你懂的!");
    11 
    12             Console.WriteLine("我是主线程,我不会被阻塞!");
    13             Console.Read();
    14         }
    15 
    16         static void client_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
    17         {
    18             Console.WriteLine("\n" + e.UserState);
    19         }
    20     }

    先前也说了,task是非常灵活的,那么针对这种异步模型,我们该如何封装成task来使用,幸好framework中提供了TaskCompletionSource来帮助

    我们快速实现。

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.IO;
     6 using System.Threading.Tasks;
     7 using System.Net;
     8 using System.ComponentModel;
     9 
    10 namespace ConsoleApplication4
    11 {
    12     class Program
    13     {
    14         static void Main()
    15         {
    16             var downloadTask = DownLoadFileInTask(
    17                     new Uri(@"http://www.7720mm.cn/uploadfile/2010/1120/20101120073035736.jpg")
    18                     , "C://1.jpg");
    19 
    20             downloadTask.ContinueWith(i =>
    21             {
    22                 Console.WriteLine("图片:" + i.Result + "下载完毕!");
    23             });
    24 
    25             Console.WriteLine("我是主线程,我不会被阻塞!");
    26 
    27             Console.Read();
    28         }
    29 
    30         static Task<string> DownLoadFileInTask(Uri address, string saveFile)
    31         {
    32             var wc = new WebClient();
    33 
    34             var tcs = new TaskCompletionSource<string>(address);
    35 
    36             //处理异步操作的一个委托
    37             AsyncCompletedEventHandler handler = null;
    38 
    39             handler = (sender, e) =>
    40             {
    41                 if (e.Error != null)
    42                 {
    43                     tcs.TrySetException(e.Error);
    44                 }
    45                 else
    46                 {
    47                     if (e.Cancelled)
    48                     {
    49                         tcs.TrySetCanceled();
    50                     }
    51                     else
    52                     {
    53                         tcs.TrySetResult(saveFile);
    54                     }
    55                 }
    56 
    57                 wc.DownloadFileCompleted -= handler;
    58             };
    59 
    60             //我们将下载事件与我们自定义的handler进行了关联
    61             wc.DownloadFileCompleted += handler;
    62 
    63             try
    64             {
    65                 wc.DownloadFileAsync(address, saveFile);
    66             }
    67             catch (Exception ex)
    68             {
    69                 wc.DownloadFileCompleted -= handler;
    70 
    71                 tcs.TrySetException(ex);
    72             }
    73 
    74             return tcs.Task;
    75         }
    76     }
    77 }


  • 相关阅读:
    LeetCode--011--盛最多水的容器(java)
    LeetCode--008--字符串转换整数 (atoi)(java)
    TF-IDF的解释
    Ubuntu14.04LTS下安装Node.js&NPM以及个人博客hexo的初始化配置
    UVa10791
    唯一分解定理
    UVa10375
    ubuntu下codeblocks安装与中文化
    Uva11582
    Ubuntu下使用网易云音乐
  • 原文地址:https://www.cnblogs.com/ShaYeBlog/p/2682109.html
Copyright © 2011-2022 走看看