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 }


  • 相关阅读:
    智器SmartQ T7实体店试用体验
    BI笔记之SSAS库Process的几种方案
    PowerTip of the Day from powershell.com上周汇总(八)
    PowerTip of the Day2010071420100716 summary
    PowerTip of the Day from powershell.com上周汇总(十)
    PowerTip of the Day from powershell.com上周汇总(六)
    重新整理Cellset转Datatable
    自动加密web.config配置节批处理
    与DotNet数据对象结合的自定义数据对象设计 (二) 数据集合与DataTable
    在VS2003中以ClassLibrary工程的方式管理Web工程.
  • 原文地址:https://www.cnblogs.com/ShaYeBlog/p/2682109.html
Copyright © 2011-2022 走看看