博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【转载】.NET(C#): Task.Unwrap扩展方法和async Lambda
阅读量:7294 次
发布时间:2019-06-30

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

目录

 

Task.Unwrap基本使用

这个扩展方法定义在TaskExtensions类型中,命名空间在System.Threading.Tasks。Unwrap会把嵌套的Task<Task>或者Task<Task<T>>的结果提取出来。

就像这样,不用Unwrap的话:

staticvoid Main(string[] args){    doo();Task.Delay(-1).Wait();}staticasyncvoid doo(){//运行嵌套的Task//Task返回Task
>//第一个await后result类型为Task
var result =awaitTask.Run
>(() => {var task =Task.Run
(() => {Task.Delay(1000).Wait();return"Mgen"; });return task; });//第二个await后才会返回stringConsole.WriteLine(await result);}

 

 

使用Unwrap后,结果可以直接从嵌套Task中提取出来:

staticasyncvoid doo(){//运行嵌套的Task//Task返回Task
>//await后类型为Task
,Unwrap后result类型为stringvar result =awaitTask.Run
>(() => {var task =Task.Run
(() => {Task.Delay(1000).Wait();return"Mgen"; });return task; }).Unwrap();//不需要await,result已经是stringConsole.WriteLine(result);}

 

 

Task.Factory.StartNew和Task.Run的Unwrap操作

简单地讲,Task.Factory.StartNew和Task.Run区别之一就有Task.Run会自动执行Unwrap操作,但是Task.Factory.StartNew不会,Task.Run就是Task.Factory.StartNew的更人性化封装,而Task.Factory.StartNew则是原始的执行。(另外关于更多的区别,推荐PFX Team的一篇非常给力的文章:)。

 

通过代码来验证:

var task1 =Task.Factory.StartNew(async () =>"Mgen");var task2 =Task.Run(async () =>"Mgen");Console.WriteLine(task1.GetType());Console.WriteLine(task2.GetType());

 

 

输出:

System.Threading.Tasks.Task`1[System.Threading.Tasks.Task`1[System.String]] System.Threading.Tasks.UnwrapPromise`1[System.String]

 可以看到

使用Task.Factory.StartNew会返回原始的Task<Task<string>>。但是Task.Run则会直接返回async Lambda的结果,中间的Unwrap操作会自动进行。

 

 

使用案例:LINQ中的async Lambda

文章讲述到这里,或许读者在想上述情况会不会很少见?不,任何使用async Lambda的情况都可能会出现上述情况,比如最近在搞一个WinRT的项目,使用LINQ去转换一些数据,但是许多WinRT的API只有异步执行的,这类问题就会出现,下方示例:

 

我们来进行一个再简单不过的LINQ Select操作,把一堆int转换成string,只不过转换过程是异步的,来看代码:

staticvoid Main(string[] args){    doo();Task.Delay(-1).Wait();}staticvoid doo(){//int数据var ints =Enumerable.Range(1, 10);//转换并输出结果foreach (var str in ints.Select(async i =>await Int2StringAsync(i)))Console.WriteLine(str);}//异步将int转换成stringstaticasyncTask
Int2StringAsync(int i){returnawaitTask.Run
(() => i.ToString());}

 

 上面代码正确吗?很多人会认为没问题的,Select方法通过一个async Lambda调用异步转换方法,并使用await异步等待结果,那么async Lambda返回string,str类型也是string,最后输出所以字符串。

 

但事实上程序运行后会输出:

System.Threading.Tasks.Task`1[System.String] System.Threading.Tasks.Task`1[System.String] System.Threading.Tasks.Task`1[System.String] System.Threading.Tasks.Task`1[System.String] System.Threading.Tasks.Task`1[System.String] System.Threading.Tasks.Task`1[System.String] System.Threading.Tasks.Task`1[System.String] System.Threading.Tasks.Task`1[System.String] System.Threading.Tasks.Task`1[System.String] System.Threading.Tasks.Task`1[System.String]

 

str变量根本不是string,而是Task<string>。上边的推断错在(上面黄字标注的)“async Lambda返回string”,async Lambda的结果并没有被await,上面的await仅仅是对Int2StringAsync方法的异步等待,而async Lambda本身仍然会返回Task<string>,所以Select会返回一些列的Task<string>。

 

最简单的解决方案是,在处理结果的时候加上await,如下:

//str的类型事实上是:Task
Console.WriteLine(str);

 这是最好的方法(如果能这样的话),因为有了这个await,事实上整个转换过程就异步化了!

 

当然如果你想在LINQ Select中直接返回结果string而不是Task<string>:

那么还有一种解决方案就是不使用async Lambda,就不存在嵌套Task的问题,直接在Select中返回异步方法的Task的Result属性:

//int数据var ints =Enumerable.Range(1, 10);//Select调用异步方法IEnumerable
strs = ints.Select(i => Int2StringAsync(i).Result);

 

 

如果一定要使用async Lambda,则必须将嵌套的Task进行Unwrap。(当然这里更多的是为了讨论技术本身,实际工作中没必要这么钻牛角尖呵呵。)

结合上面讲到的知识,使用Task.Factory.StartNew需要进行一个Unwrap,然后返回Task<T>的结果作为Select方法的最终返回值,代码:

//int数据var ints =Enumerable.Range(1, 10);//Select调用异步方法IEnumerable
strs = ints.Select(i =>Task.Factory.StartNew(async () =>await Int2StringAsync(i)).Unwrap().Result);

 

 

而Task.Run的话,不需要Unwrap:

IEnumerable
strs = ints.Select(i =>Task.Run(async () =>await Int2StringAsync(i)).Result);

 

 

转载于:https://www.cnblogs.com/baobaodong/p/4179992.html

你可能感兴趣的文章
thinkphp5中资源文件路径的使用
查看>>
国内勒索病毒疫情严重 每天十多万台电脑被感染
查看>>
【云周刊】第150期:阿里云发布首款全球智能互联的网络产品——云骨干网
查看>>
Jenkins - Extended E-mail配置教程
查看>>
干货 | 一文带你搞定Python 数据可视化
查看>>
管理和安装 chart - 每天5分钟玩转 Docker 容器技术(168)
查看>>
AI真的会杀人?DeepMind开发了二维网格游戏来做测试
查看>>
JSP完全自学手册图文教程
查看>>
MESI协议
查看>>
dubbo-provider-deploy
查看>>
shell特殊符号cut命令、sort_wc_uniq命令、tee_tr_split命令、shell特殊符号下
查看>>
html固定table表头的实现思路
查看>>
用java将excel数据导入txt
查看>>
五周第二次课
查看>>
HTTP面试题都在这里
查看>>
解决zabbix-agent二进制班不能连接使用docker搭建的zabbix-server
查看>>
密码协议(三)裁决协议和自动执行协议
查看>>
JAVA中关于并发的一些理解
查看>>
Java springcloud B2B2C o2o多用户商城 springcloud架构 (十七)上传文件
查看>>
SpringBoot集成Redis--配置自定义的RedisCacheManager
查看>>