批判“await使用中的阻塞和并发”——对asyc/await这对基友的误会和更正
写第一篇《await使用中的阻塞和并发》的时候还自信满满,觉得写的真不错,结果漏洞百出……
更正第二篇《await使用中的阻塞和并发(二)》的时候觉得这回不会再错了……
结果我正在写第三篇,而且连篇名都不敢延用了……
首先完善第二篇对Foreach(Action<T>)的拆解,用很厉害的小兄弟geelaw的话说就是“是用另一个方法返回λ表达式创建的委托,并未把λ表达式换成方法。”惭愧啊,在小兄弟的指点下,修改代码如下,在Foreach(Action)这个话题上算是圆满了:
public void AwaitFuncTaskListNoLambda()
{
var funcList = new List<Func<Task>>()
{
Delay3000Async,
Delay2000Async,
Delay1000Async
}; //funcList.ForEach(async _ => await _());
//funcList.ForEach(AwaitAction());
//foreach (var func in funcList)
//{
// AwaitAction()(func);
//} Action<Func<Task>> L = this.Xxx;
foreach (var func in funcList)
{
L(func);
}
} async void Xxx(Func<Task> ft)
{
await ft();
} public Action<Func<Task>> AwaitAction()
{
return async _ => await _();
}
接下来的文字是本篇的重点,来自redjackwang菊苣的指点,他指出了我第二篇中仍存在的误区和错误。下面让我们来领略一下大牛的风采。首先从上篇我给出的结论入手:
- async用于异步,可以优美的替代Thread、BackgroundWorker和Task.Run等写法。
- await用于等待。一定是在你主动希望阻塞并等待返回结果时才使用。
- 在async方法里,Task在创建时就开始运行了。
第一点需要指出这里的替代仅仅是写法的变化,async方法内部具体实现可能仍使用Thread等多线程技术来实现。这里需要明确async/await仅是一个异步的写法,而Thread\BackgroundWorker和Task是多线程。
关于第二点,有两位菊苣分别提出异议。redjackwang的原话是“await并不会阻塞任何线程。await只是把当前方法状态保存并立即返回,不管是主线程还是后台线程都不会阻塞,而是在完成时call一个回调。”胖胖的韦恩卑鄙是这么说的:“说await是主动阻塞问题很大。那叫做响应式休眠?”
至于第三点,那是MSDN坑我,redjackwang大人竟然从MSDN上找到了更详细的答案来证明我给出的链接是错误的。在此一定要共享出来给各位,不要被我上篇错误的结论误导了,正确的表述如下:
Tasks created by its public constructors are referred to as “cold” tasks, in that they begin their life cycle in the non-scheduled TaskStatus.Created state, and it’s not until Start is called on these instances that they progress to being scheduled. All other tasks begin their life cycle in a “hot” state, meaning that their asynchronous execution has already been initiated and their TaskStatus is an enumeration value other than Created.
All tasks returned from TAP methods must be “hot.” If a TAP method internally uses a Task’s constructor to instantiate the task to be returned, the TAP method must call Start on the Task object prior to returning it. Consumers of a TAP method may safely assume that the returned task is “hot,” and should not attempt to call Start on any Task returned from a TAP method. Calling Start on a “hot” task will result in an InvalidOperationException (this check is handled automatically by the Task class).
为此redjackwang还给出了证明的例子,在这个例子中,如果注释掉start方法,Task是不会自动运行的。结论是Task如果是通过构造函数创建的,状态是cold的,不会自动运行,而前一篇都是通过返回Task的方法创建出来的,状态是hot,所以自动运行了,和在不在async方法中没有关系。
private async void Button_Click(object sender, RoutedEventArgs e)
{
var v = await Foo();
this.Title = v.ToString();
} private async Task<long> Foo()
{
var t = new Task<long>(() =>
{
long z = ;
for (int i = ; i < ; i++)
{
z += i;
}
return z;
});
//t.Start();
return await t;
}
本篇应不是最终的结果,后面如有进一步的发现仍会更新下去,批判的暴风雨还在继续……
批判“await使用中的阻塞和并发”——对asyc/await这对基友的误会和更正的更多相关文章
- await使用中的阻塞和并发
本文讨论,通过将Lambda还原成最普通的代码段,来解释上篇提出的疑问.并更正上篇中一些不太正确的写法.最后会给出无需等待Async方法返回值时,对Async方法使用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. 引言 在并发编程中我们有时候需要使用线程安全的队列. 如果我们要实现一个线程安全的队列有两种实现方式:一种是使用阻塞算法,另一种是使用非阻塞算法. 使用阻塞算法的队列可以用一个锁(入队和出 ...
- js异步回调Async/Await与Promise区别 新学习使用Async/Await
Promise,我们了解到promise是ES6为解决异步回调而生,避免出现这种回调地狱,那么为何又需要Async/Await呢?你是不是和我一样对Async/Await感兴趣以及想知道如何使用,下面 ...
- await在forEach不起作用解决【await is a reserved word】
原文链接:https://blog.csdn.net/ssbb1995/article/details/82084800 1.await 只能在 async中使用,如: async function ...
- Iterator与Asyc/Await的实现
https://wanago.io/2018/04/23/demystifying-generators-implementing-async-await/
- 今天写了一个可以测试并发数和运行次数的压力测试代码。(Java)
今天写了一个可以测试并发数和运行次数的压力测试代码 介绍一下为什么会写这么一个工具. 介绍一个这个工具怎么用的. 背景 最近在开发CoapServer端,以及模拟设备侧发送数据调用开发好的CoapSe ...
随机推荐
- FDQuery sqlserver 临时表
用FDQuery执行创建临时表,查不到临时表,用ADOQuery和BDEQuery均正常,比较发现用ADOQuery执行的时候只有SQL没有调用sql的系统存储过程sp_prepexec. 是fdqu ...
- Android 包信息工具类
/** AndroidInfoUtils:安卓游戏包信息工具类**/ 1 public class AndroidInfoUtils { @SuppressWarnings("uncheck ...
- svg make a face
1.创建项目 #使用simple模板 vue init webpack-simple vue-svg #安装依赖 cd vue-svg/ npm i #安装d3 npm i d3 --save 2.代 ...
- 2015年传智播客JavaEE 第168期就业班视频教程06-权限校验子系统介绍
没整过论坛你也整过淘宝,其实淘宝登录的也分商家和个人,卖家和买家,不同的人登录显示的东西是不一样的.权限系统要分两大过程,第四天上午下午分开,分为授权与校验.我把某一个职务给你叫做授权,例如封你为征西 ...
- 48. Rotate Image (Array)
You are given an n x n 2D matrix representing an image. Rotate the image by 90 degrees (clockwise). ...
- c语言define和typedef区别和使用
define完全可以理解替换,typedef代表别名.听着差不多的意思,那2者区别在哪? 先来个简单例子查看基本使用. //define和typedef区别 #define DB double //替 ...
- Vue.js基础知识
<!DOCTYPE html> <html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml&q ...
- 如果应用程序正在通过 <identity impersonate="true"/> 模拟,则标识将为匿名用户(通常为 IUSR_MACHINENAME)或经过身份验证的请求用户。
在配置文件中添加 <identity impersonate= "true " userName= "Administrator " pass ...
- Golang之fmt格式“占位符”
golang的fmt包实现了格式化I/O函数: package main import "fmt" type Human struct { Name string } func m ...
- KMP算法,查询匹配串的个数
想不到时隔两年回来重新学习KMP算法还是那么难,不过理解了大概,把例程贴上来,如果是求数量只需要加个count变量记录即可. #include"stdio.h" #include& ...