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(3000);
Console.WriteLine(3000);
Console.WriteLine(DateTime.Now);
}
public async Task Delay2000Async()
{
await Task.Delay(2000);
Console.WriteLine(2000);
Console.WriteLine(DateTime.Now);
}
public async Task Delay1000Async()
{
await Task.Delay(1000);
Console.WriteLine(1000);
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() www.yztrans.com
{
var funcList = new List<Func<Task》()
{
Delay3000Async,
Delay2000Async,
Delay1000Async
};
foreach (var func in funcList)
{
func();
}
}
接下来是总结陈述:
async用于异步,可以优美的替代Thread、BackgroundWorker和Task.Run等写法。
await用于等待。一定是在你主动希望阻塞并等待返回结果时才使用。
在async方法里,Task在创建时就开始运行了 www.lefeng123.com
写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 ...
- 深入理解java:2.3.4. 并发编程concurrent包 之容器ConcurrentLinkedQueue(非阻塞的并发队列---循环CAS)
1. 引言 在并发编程中我们有时候需要使用线程安全的队列. 如果我们要实现一个线程安全的队列有两种实现方式:一种是使用阻塞算法,另一种是使用非阻塞算法. 使用阻塞算法的队列可以用一个锁(入队和出 ...
- Java并发(十八):阻塞队列BlockingQueue
阻塞队列(BlockingQueue)是一个支持两个附加操作的队列. 这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空.当队列满时,存储元素的线程会等待队列可用. 阻塞队列常用于生产 ...
- Java并发(10)- 简单聊聊JDK中的七大阻塞队列
引言 JDK中除了上文提到的各种并发容器,还提供了丰富的阻塞队列.阻塞队列统一实现了BlockingQueue接口,BlockingQueue接口在java.util包Queue接口的基础上提供了pu ...
- java多线程并发执行demo,主线程阻塞
其中有四个知识点我单独罗列了出来,属于多线程编程中需要知道的知识: 知识点1:X,T为泛型,为什么要用泛型,泛型和Object的区别请看:https://www.cnblogs.com/xiaoxio ...
- [译]async/await中阻塞死锁
这篇博文主要是讲解在async/await中使用阻塞式代码导致死锁的问题,以及如何避免出现这种死锁.内容主要是从作者Stephen Cleary的两篇博文中翻译过来. 原文1:Don'tBlock o ...
随机推荐
- 【HDOJ】5017 Ellipsoid
简单地模拟退火. /* 5017 */ #include <cstdio> #include <cstring> #include <cstdlib> #inclu ...
- Borg Maze(bfs+prim)
Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 6971 Accepted: 2345 Description The B ...
- Python datetime时间日期处理
在处理微博数据的时候,经常会遇到对时间数据的处理,于是决定开始去一步一步的总结下Python关于时间的常见处理,主要以参考Python的官方文档为主,结合实例.接下来会一步一步更新,敬请期待
- C/C++中如何获取数组的长度?
C.C++中没有提供 直接获取数组长度的函数,对于存放字符串的字符数组提供了一个strlen函数获取长度,那么对于其他类型的数组如何获取他们的长度呢?其中一种方法是使 用sizeof(array) / ...
- Purchase Document Open Interface(PDOI)
PO模块也有自己的接口表,多用于把其他业务系统在Oracle EBS系统生成采购订单记录. Table Name Description Type PO_HEADERS_INTERFACE This ...
- Oracle sqlplus 语法
目录: 0. FREFACE 1. 执行一个SQL脚本文件 2. 对当前的输入进行编辑 3. 重新运行上一次运行的sql语句 4. 将显示的内容输出到指定文件 5. 关闭spool输出 6.显示一个表 ...
- 遍历INI文件和删除指定域内容
主要还是使用的INI文件操作的API,只是把参数修改下. BOOL WINAPI WritePrivateProfileString( __in LPCTSTR lpAppName, __in LPC ...
- 工作那些事(四)大公司VS小公司
都说大公司学做人,小公司学做事.这话有一定的道理,但是也不是绝对的.做人简单地说就是与人打交道,为人处世:做事,对我们来说就是实现需求,解决bug.有句话,是这么说的:有人的地方就有江湖,有江湖,就得 ...
- atitit。浏览器缓存机制 and 微信浏览器防止缓存的设计 attilax 总结
atitit.浏览器缓存机制 and 微信浏览器防止缓存的设计 attilax 总结 1. 缓存的一些机制 1 1.1. http 304 1 1.2. 浏览器刷新的处理机制 1 1.3. Expir ...
- linux系统应用--Linux下用virtualBox安装win7(共享文件夹)
1. deepin终端: sudo apt-get install virtualbox 2. 下载win7 iso文件 3. deepin终端启动virtualbox : ./virtualbo ...