博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
8天玩转并行开发——第六天 异步编程模型
阅读量:6941 次
发布时间:2019-06-27

本文共 7878 字,大约阅读时间需要 26 分钟。

原文 

   在.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
(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
;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
(i => 6 { 7 return i + "i can fly"; 8 }); 9 10 Task
.Factory.FromAsync(func.BeginInvoke, func.EndInvoke, "yes,", null).ContinueWith11 (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
.Factory.FromAsync(fs.BeginRead, fs.EndRead, b, 0, b.Length, null, TaskCreationOptions.None)12 .ContinueWith13 (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
> taskList = new List
>(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
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
task = Task
.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“的

概念,具体内容可以参照百科:  ,有幸的是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 ConsoleApplication411 {12     class Program13     {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
DownLoadFileInTask(Uri address, string saveFile)31 {32 var wc = new WebClient();33 34 var tcs = new TaskCompletionSource
(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 else46 {47 if (e.Cancelled)48 {49 tcs.TrySetCanceled();50 }51 else52 {53 tcs.TrySetResult(saveFile);54 }55 }56 57 wc.DownloadFileCompleted -= handler;58 };59 60 //我们将下载事件与我们自定义的handler进行了关联61 wc.DownloadFileCompleted += handler;62 63 try64 {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 }

转载地址:http://uwinl.baihongyu.com/

你可能感兴趣的文章
Nginx 学习笔记(二)Web 服务启用 HTTP/2
查看>>
layer web 弹窗
查看>>
JVM GC算法 CMS 详解(转)
查看>>
mysql输入密码后闪退怎么办?
查看>>
mysql 文件导入方法总结
查看>>
汉化入门之ExplorerControls
查看>>
python 转 exe -- py2exe库实录
查看>>
第 55 章 Cherokee
查看>>
iOS - Plist 数据解析
查看>>
sql 经常使用的语句(个人)
查看>>
日志管理之 Docker logs - 每天5分钟玩转 Docker 容器技术(87)
查看>>
查看Linux下的文件
查看>>
7mall:4种方法弥补店铺亮点不够多的产品
查看>>
SAP WM LRFMD中Variant参数的影响初探
查看>>
【Xamarin挖墙脚系列:多窗口之间的导航】
查看>>
JPA & Hibernate 注解
查看>>
android 读写sd卡的权限设置
查看>>
Android4: Write Storage权限问题
查看>>
9.9、Libgdx之软键盘
查看>>
LB 负载均衡的层次结构(转)
查看>>