await使用中的阻塞和并发(二)
本文继续上篇未完成的讨论,通过将Lambda还原成最普通的代码段,来解释上篇提出的疑问。并更正上篇中一些不太正确的写法。最后会给出无需等待Async方法返回值时,对Async方法使用await的建议,供大家参考。
第一篇传送门:await使用中的阻塞和并发
首先我们比较以下三段代码,其中第一和第三可以做到并发执行,第二个是线性的执行。
//并发
public async Task Await3Task()
{
var task3 = Delay3000Async();
var task2 = Delay2000Async();
var task1 = Delay1000Async(); await task3;
await task2;
await task1;
} //非并发
public async Task Await3DelayAsync()
{
await Delay3000Async();
await Delay2000Async();
await Delay1000Async();
} //并发,这里甚至可以把var task3等去掉,直接调用xxxAsync(),只是会出现警告的波浪罢了
public void NoAwait3Task()
{
var task3 = Delay3000Async();
var task2 = Delay2000Async();
var task1 = Delay1000Async();
}
//这里补充一下调用的三个Async方法 public async Task Delay3000Async()
{
await Task.Delay();
Console.WriteLine();
Console.WriteLine(DateTime.Now);
} public async Task Delay2000Async()
{
await Task.Delay();
Console.WriteLine();
Console.WriteLine(DateTime.Now);
} public async Task Delay1000Async()
{
await Task.Delay();
Console.WriteLine();
Console.WriteLine(DateTime.Now);
}
这里我们可以看出,await和并发木有关系,隐式的并发执行是由async方法决定的。而await是用于主动的阻塞,期望等待方法结束才继续运行时使用。
以下2个方法的执行结果是一样的,都可以并发执行。可以看出,把仅仅希望并发执行,不需要返回结果的方法丢到List里,然后Foreach是毫无意义的……所以上篇其实是干了一些蠢事情……
public void AwaitTaskList()
{
var tasks = new List<Task>
{
Delay3000Async(),
Delay2000Async(),
Delay1000Async()
}; tasks.ForEach(async _ => await _);
} public void NoAwaitTaskList()
{
var tasks = new List<Task>
{
Delay3000Async(),
Delay2000Async(),
Delay1000Async()
};
}
上篇提到Task在创建的时候,就已经开始运行了。而await仅仅是出现在需要等待结果的地方。所以如果无需等待,就不要写await了……貌似上篇又干了一些蠢事……
//非并发
public async Task Await3Func()
{
Func<Task> func3 = Delay3000Async;
Func<Task> func2 = Delay2000Async;
Func<Task> func1 = Delay1000Async; await func3();
await func2();
await func1();
}
//并发
public void NoAwait3Func()
{
Func<Task> func3 = Delay3000Async;
Func<Task> func2 = Delay2000Async;
Func<Task> func1 = Delay1000Async; func3();
func2();
func1();
}
同时我本人对List<Func<Task>>尚未创建Task却可以并发表示疑问,接下来给出解答。下面分别贴出了使用Lambda和不使用的情况。我们可以清楚的看到Lambda表达式具体帮我们省略了什么。
//使用Lambda
public void AwaitFuncTaskList()
{
var funcList = new List<Func<Task>>()
{
Delay3000Async,
Delay2000Async,
Delay1000Async
}; funcList.ForEach(async _ => await _());
}
//不使用Lambda
public void AwaitFuncTaskListNoLambda()
{
var funcList = new List<Func<Task>>()
{
Delay3000Async,
Delay2000Async,
Delay1000Async
}; //funcList.ForEach(AwaitAction());
foreach(var func in funcList)
{
AwaitAction()(func);
}
} public Action<Func<Task>> AwaitAction()
{
return async _ => await _();
}
根据上文的总结,可以看出虽然await造成了阻塞,但并不是在主线程等待,所以我们幸运的并发了……
再看下面一段,我干脆拿掉了await,毫无疑问的并发执行了。上篇让人汗颜的事情貌似还干了不少,好在我脸皮厚,不会删掉前一篇的随笔,哈哈哈哈……
public void NoAwaitFuncTaskList()
{
var funcList = new List<Func<Task>>()
{
Delay3000Async,
Delay2000Async,
Delay1000Async
}; funcList.ForEach(_ => _());
} public void NoAwaitFuncTaskListNoLambda()
{
var funcList = new List<Func<Task>>()
{
Delay3000Async,
Delay2000Async,
Delay1000Async
}; //funcList.ForEach(NoAwaitAction());
foreach(var func in funcList)
{
NoAwaitAction()(func);
}
} public Action<Func<Task>> NoAwaitAction()
{
return _ => _();
}
仔细看一下可以发现,为了懒惰而使用的ForEach其实增加了多余的一层Action<Func<Task>>,如果直接使用foreach会是如下的情况:
public void NoAwaitFuncTaskWithoutForeachExtension()
{
var funcList = new List<Func<Task>>()
{
Delay3000Async,
Delay2000Async,
Delay1000Async
}; foreach (var func in funcList)
{
func();
}
}
接下来是总结陈述:
- async用于异步,可以优美的替代Thread、BackgroundWorker和Task.Run等写法。
- await用于等待。一定是在你主动希望阻塞并等待返回结果时才使用。
- 在async方法里,Task在创建时就开始运行了。
- 写Lamdba别把自己写晕了……
await使用中的阻塞和并发(二)的更多相关文章
- 批判“await使用中的阻塞和并发”——对asyc/await这对基友的误会和更正
写第一篇<await使用中的阻塞和并发>的时候还自信满满,觉得写的真不错,结果漏洞百出…… 更正第二篇<await使用中的阻塞和并发(二)>的时候觉得这回不会再错了…… 结果我 ...
- await使用中的阻塞和并发
本文讨论,通过将Lambda还原成最普通的代码段,来解释上篇提出的疑问.并更正上篇中一些不太正确的写法.最后会给出无需等待Async方法返回值时,对Async方法使用await的建议,供大家参考.第一 ...
- [译]async/await中使用阻塞式代码导致死锁 百万数据排序:优化的选择排序(堆排序)
[译]async/await中使用阻塞式代码导致死锁 这篇博文主要是讲解在async/await中使用阻塞式代码导致死锁的问题,以及如何避免出现这种死锁.内容主要是从作者Stephen Cleary的 ...
- [译]async/await中使用阻塞式代码导致死锁
原文:[译]async/await中使用阻塞式代码导致死锁 这篇博文主要是讲解在async/await中使用阻塞式代码导致死锁的问题,以及如何避免出现这种死锁.内容主要是从作者Stephen Clea ...
- 阻塞队列之二:LinkedTransferQueue
一.LinkedTransferQueue简介 TransferQueue是一个继承了BlockingQueue的接口,并且增加若干新的方法.LinkedTransferQueue是TransferQ ...
- Linux非阻塞IO(二)网络编程中非阻塞IO与IO复用模型结合
上文描述了最简易的非阻塞IO,采用的是轮询的方式,这节我们使用IO复用模型. 阻塞IO 过去我们使用IO复用与阻塞IO结合的时候,IO复用模型起到的作用是并发监听多个fd. 以简单的回射服务器 ...
- 深入理解java:2.3.4. 并发编程concurrent包 之容器ConcurrentLinkedQueue(非阻塞的并发队列---循环CAS)
1. 引言 在并发编程中我们有时候需要使用线程安全的队列. 如果我们要实现一个线程安全的队列有两种实现方式:一种是使用阻塞算法,另一种是使用非阻塞算法. 使用阻塞算法的队列可以用一个锁(入队和出 ...
- Asycn/Await 异步编程初窥(二)
经过总过4天的学习和实践,做完了 WinForm 下 .Net 4.5 的基本异步应用,实现了一个 Http 协议下载的测试程序,为以后使用 .Net 4.5 积累知识和经验.这个小程序完成这样几个作 ...
- 并发(二)CyclicBarrier
CyclicBarrier 循环屏障,用于一组固定数目的线程互相等待.使用场景如下: 主任务有一组串行的执行节点,每个节点之间有一批任务,固定数量的线程执行这些任务,执行完成后,在节点完成集合后,再继 ...
随机推荐
- Loadrunner11在新建Microsoft Word 报告时提示指定的转换无效
HP Loadrunner11中文教程的学习基本已经结束,最后困扰我的就是这个在创建Microsoft Word 报告时不停的提示“指定的转换无效”的问题.在网上搜索了好长时间,好多朋友回答说没有生成 ...
- BTM事务配置
请参考原贴:http://thinkdifferent.iteye.com/blog/1450433 Tomcat6上配置BTM 博客分类: practice tomcatjava )去http:// ...
- 「小程序JAVA实战」小程序视频展示页开发(52)
转自:https://idig8.com/2018/09/22/xiaochengxujavashizhanxiaochengxushipinzhanshiyekaifa51/ 这次说下,小程序的视频 ...
- Spring JDBC Framework详解——批量JDBC操作、ORM映射
转自:https://blog.csdn.net/yuyulover/article/details/5826948 一.spring JDBC 概述 Spring 提供了一个强有力的模板类JdbcT ...
- 想取得刚才nextval()的值时,放心大胆的用currval()吧,currval()的返回值并不会因为nextval()的并发访问而混乱
以前写sql的时候总是担心current()得到的值并不会绝对等于我上一次nextval()取得的值;因为可能其他线程并发访问nextval(). 先说结论吧:当你拿到一个数据库连接,先nextval ...
- java CPU 100% 排查
一个应用占用CPU很高,除了确实是计算密集型应用之外,通常原因都是出现了死循环. (友情提示:本博文章欢迎转载,但请注明出处:hankchen,http://www.blogjava.net/hank ...
- 分享 - 普通程序员如何转向AI方向
原作者:计算机的潜意识 原文链接,内容稍有改动,侵删 1. 目的2. AI领域简介3. 学习方法4. 学习路线 0) 领域了解1) 知识准备2) 机器学习3) 实践做项目4) 深度学习5) 继续机器学 ...
- 迷你MVVM框架 avalonjs 沉思录 第1节 土耳其开局
#cnblogs_post_body p{ text-indent:2em; margin-top: 1em; } 正如一切传说的开端那样,有一远古巨神开天辟地,然后就是其他半神喧宾夺主.我们对最巨贡 ...
- HBase实验(CRUD和MR入库)
目录 前期准备 在HBase shell中实现CRUD操作 1. 启动命令行客户端 2. 创建表 3. 删除.新增列族 4. 删除表teacher 5. 新增数据 6. 查看数据 用Java API实 ...
- php导出数组到csv格式demo
php的二维数组导出到csv需要处理文字编码,代码如下 <?php $data=array( array("username"=>"test1",& ...