惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

美团技术团队
Attack and Defense Labs
Attack and Defense Labs
Google Online Security Blog
Google Online Security Blog
SecWiki News
SecWiki News
N
News and Events Feed by Topic
O
OpenAI News
Application and Cybersecurity Blog
Application and Cybersecurity Blog
AI
AI
L
LINUX DO - 最新话题
S
Securelist
Cisco Talos Blog
Cisco Talos Blog
V
Vulnerabilities – Threatpost
Webroot Blog
Webroot Blog
T
Threatpost
A
Arctic Wolf
罗磊的独立博客
T
Tor Project blog
The Hacker News
The Hacker News
C
Cybersecurity and Infrastructure Security Agency CISA
N
News | PayPal Newsroom
Latest news
Latest news
Y
Y Combinator Blog
S
Schneier on Security
T
Troy Hunt's Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
S
Security @ Cisco Blogs
量子位
F
Fortinet All Blogs
Blog — PlanetScale
Blog — PlanetScale
Jina AI
Jina AI
L
Lohrmann on Cybersecurity
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Help Net Security
Help Net Security
腾讯CDC
The Last Watchdog
The Last Watchdog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
P
Proofpoint News Feed
Cloudbric
Cloudbric
Simon Willison's Weblog
Simon Willison's Weblog
AWS News Blog
AWS News Blog
NISL@THU
NISL@THU
GbyAI
GbyAI
B
Blog
Spread Privacy
Spread Privacy
宝玉的分享
宝玉的分享
S
Secure Thoughts
P
Palo Alto Networks Blog
Last Week in AI
Last Week in AI
D
Docker
酷 壳 – CoolShell
酷 壳 – CoolShell

博客园 - ProjectDD

NET 中 Async/Await 的演进:从状态机到运行时优化的 Continuation C# 指针用法小结 C# BinaryPrimitives 类 C# 内存对齐 linq 查询关于 from子句 关于排序算法 C# Dev Kit 经常导致崩溃 不太会用Span<T> 看文档上的优点估摸着试试 span,memory,ArrayPool,MemoryPool,等的性能对比 C# simd 性能雷点记录 C# 模式匹配里应该注意的几点 高中生理解梯度为何是方向导数极大值 概略 deep net 通过relu 进行函数逼近 win10 下安装 rust 的 依赖配置,通过vs2022 C# 有多需要aot 24个希腊字母的中文拼音版 英语发音探讨 sagemath 9.x 下的 jupyter 工作路径设置 Serializing delegates is not supported on this platform
关于C# await的一点新理解
ProjectDD · 2024-01-31 · via 博客园 - ProjectDD

关于await又理解深一点了,以前有点懵,
原来await 是对Task.Run的一个修饰(更准确的说是对Task实例的一个修饰,因为Task实例是必然会包装一个委托的所以就实现了对方法的包装,当这个方法调用时也就是Run时你可以选择是否使用await),叶节点,后续技节点是对标有async的方法进行串烧修饰,所以根在Task.Run这个方法要所以要理解await必须要详细查看Task.Run
Task.Run是调用后直接把Run的参数委托丢线程池的然后不等待直接返回的,所以返回是一个Task/Task<T>只有这两种情况加上await以后 就等待,然后直接取出Task<T>中的T了,这中间的各种可能的隐藏操作被底层编译器,运行时等代替了,比如线程上下文切换等。

要注意其实在实际场景中直接用Task.Run而不加await的情况才是异步的核心需求,异步的意思是同时做不等待。所以只有让很多Task同时开始调用的时候才有价值。

所以在同步调用方调用async方法时,最终还得用.Wait()/Result两种方式对其进行调用,从这里可以看出await其实是在异步线程池上进行的等待。因为现在有很多异步调用终端比如 webapi中的 Controller.Action方法比如 标记为async的Main方法都是异步调用终端所以不需要用上述方式切换回

同步主线程。

//...

async Task mAsync(){
    await Task1();
    await Task2();
    //...
    await Task3();
}

void m(){
    mAsync().Wait();
}

上述代码是有价值的异步设计为什么呢?因为mAsync方法里同时有多个await在,是await其实是在异步里等待它虽然在等待但是在异步里等待,这还有个好处是它可以在设计时清楚的管理异步调用的次序,要不然异步调用的次序是不可管理的要不然加很多锁或通知机制,而Task很好的把这些机制底层化为自动处理了,那么这些Task1...n之间的任务实际在调用m方法时会全部给到线程池(做统筹)从而能让它们可以在一定程度上“同步执行”来体现异步的价值,以至于在最后mAsync.Wait()时会有较短的耗时和较高的cpu资源利用率,而这就是异步编程的价值所在。

async Task m(){
  var time0=DateTime.Now;
  var t=Task.Delay(5000);
  await Task.Delay(2200);
  await Task.Delay(2500);
  await t;
  Console.WriteLine(DateTime.Now - time0);
}
await m();

// 有网友给出的代码经测试结果发现其耗时基本上在5秒多一点,而如果3个await同步执行的话需要9.7秒以上,这大概是因为这3个await之间没有依赖不会Task.ContinueWith(OtherTask)的原因如果存在这样的依赖那么时间就会是9.7以上,所以底层应该对此做了判断,这也就是说在async方法里要多用Task.Run,只要保证其中有一个await就行,await的目的是 Task.ContinueWith 让其返回可以被拿到调用环境里使用否则,则不需要await 而如果async方法有很多Task.Run但其实并不都需要await,只是你写了也无妨因为底层有一定的判断是知道的(上述测试结果就是证明),所以你可以都写,也可以只在最后一个Task.Run()前写await以满足async方法的需要。这样的设计就真正能体现异步编程带来的价值了。