Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【译文】Rust futures: async fn中的thread::sleep和阻塞调用

【译文】Rust futures: async fn中的thread::sleep和阻塞调用

作者头像
袁承兴
发布于 2020-10-10 01:28:41
发布于 2020-10-10 01:28:41
3.1K00
代码可运行
举报
运行总次数:0
代码可运行

async

原文:Rust futures: thread::sleep and blocking calls inside async fn URL: https://blog.hwc.io/posts/rust-futures-threadsleep-and-blocking-calls-inside-async-fn/

近来,关于Rust的futuresasync/await如何工作(“blockers”,哈哈),我看到存在一些普遍的误解。很多新用户为async/await带来的重大改进而感到兴奋,但是却被一些基本问题所困扰。即使有了async/await,并发依然很难。文档还在进一步充实,阻塞/非阻塞之间的交互很棘手。希望本文对你有所帮助。

(本篇主要是关于特定的痛点;有关Rust中的异步编程的概述,请转至本书

TLDR(Too Long Didn't Read):小心在async fn中使用昂贵的阻塞调用!如果不确定, 鉴于Rust std库中几乎所有都是阻塞的,所以就要注意哪些调用是耗时的!

虽然我认为任何人都可能犯这个错误(在引入足够的负载来显著地阻塞线程之前,往往察觉不到),但是初学者尤为如此。下面的场景可能有点冗长,但我认为有必要展示一下在async fn中实现阻塞调用是多么容易。

不要用 std::thread::sleep sleep

在研究了一个简单的示例之后,Rust异步新手可能要做的第一件事就是去验证程序真正实现了异步。因此,我们使用Rust异步书籍中的示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
use futures::join;async fn get_book_and_music() -> (Book, Music) {
 let book_fut = get_book();
 let music_fut = get_music();
 join!(book_fut, music_fut)
}

即使你在get_book和get_music内部打日志,也无法通过简单的方式来判断它们是同时运行的,因为任何一次运行都可能产生恰好与代码顺序匹配的输出。你必须多次运行该程序,才能查看日志记录顺序是否可以翻转(如果不翻转怎么办?)。

如果想看到get_book和get_music是100%同时运行,你可能会想到记录它们的开始时间,并查看开始时间是否相同。但是,等等,如果开始时间仍然是串行的,但fn运行得如此之快,看起来仍然像是并发该怎么办?

引入一个延迟!比如(清楚起见,使用伪码):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
async fn get_book() {
 println!("book start: time {}", current_time());
 std::thread::sleep(one_second);
 println!("book end: time {}", current_time());
}

在get_book和get_music内部延迟1秒,我们希望,如果是并发的话,则会看到以下的输出:

book start: time 0.00 music start: time 0.00 book end: time 1.00 music start: time 1.00

如果是串行,我们预期是:

book start: time 0.00 book end: time 1.00 music start: time 1.00 music end: time 2.00

你认为会发生什么, 串行或并发?

你已经读了这篇文章的标题,可能会猜到get_book和get_music是按顺序执行的。但为什么!?异步fn中的所有内容不是都应该同时运行吗?

在继续解释之前,可以看个问题已经多次被问到:

reddit 1 reddit 2 reddit 3 stackoverflow 1

因此,如果你也犯了这个错误,不用担心,其他许多人也有同样的经历。

什么搞错了?为什么async不行?

我不会在这里深入讨论futuresasync/await本书是一个很好的起点)。我只想指出造成困惑的两个可能的根源:

std::thread::sleep 会阻塞?

对于新手来说,std::thread::sleep会造成阻塞可能并不是显而易见的。尽管事后看起来很明显,但是当尝试掌握全新的程序执行范式时,却很容易忽略。

即使你大致了解并发,也可能不知道thread::sleep是具体如何实现的。一些上层推理加上一些示例(例如上述)可能会帮助你理解。但是文档中并没有明说“此调用是阻塞的,你不应该在异步上下文中使用它”,并且非系统程序员可能不会过多地考虑“将当前线程置于睡眠状态”。

(具有讽刺意味的是,如果人们的异步编程的心智模型是让Future进入“睡眠”状态从而得以让其他工作发生,那么thread::sleep可能会特别令人困惑)。

async 可以做什么?

但是有些人可能会说:“如果thread::sleep阻塞了怎么办?不是把它放在async fn中就好了吗?”

为了理解那些在线讨论,(就要知道)他们的想法是以为async可以使代码块或函数内部的所有内容异步。

首先,我想说这是有意义的;async/await存在的部分原因是它使每个人都容易进行异步操作。而且,如果你从较高的层次上理解了并发模型(事件循环,通常是尝试不阻塞线程),那么可能没有特定的理由导致async不能仅仅通过使事物定义为异步来起作用。那绝对是最简单,最符合人体工程学的方式。

不幸的是,这不是Rust的async范式的工作方式。async功能很强大,但从本质上讲,它只是提供了一种更好的处理Futures的方法。而且Future不只是自动将阻塞调用移到一边以允许完成其他工作;它要结合使用具备轮询和异步运行时这种完全独立的系统,才能进行异步舞蹈。在该系统内进行的任何阻塞调用仍将处于阻塞状态。

这可能会造成一些困惑,因为async/await允许我们编写看起来更像常规(阻塞)代码的代码。那就是async/awaitawait部分进入的地方。当你在async块中awaitfuture时,它能够将自己安排在线程外并为其他任务让路。阻塞代码可能看起来很相似,但是由于它不是future,所以无法await,也无法为其他任务腾出空间。

因此,下面不会阻塞,但是await可以让你编写看起来与阻塞调用非常相似的代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
async {
 let f = get_file_async().await;
 let resp = fetch_api_async().await;
}

下面在每行调用时阻塞:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
async {
 let f = get_file_blocking();
 let resp = fetch_api_blocking();
}

下面将不能通过编译:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
async {
 let f = get_file_blocking().await;
 let resp = fetch_api_blocking().await;
}

在这里什么也没有发生(您必须在async内部awaitfutures!):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
async {
 let f = get_file_async();
 let resp = fetch_api_async();
}

总的来说,最好将async视为允许在函数或块中 await 的东西,但实际上并不会使任何东西异步。

如何不阻塞

如果想要异步fn取消阻塞该怎么办?

你可以找到一个异步替代方案:当thread::sleep阻塞时,你可以使用它们(取决于你选择的运行时生态系统):

tokioasync_std都为其他阻塞操作(例如文件系统和tcp流访问)提供了异步替代方法。

另一个选择是将阻塞调用移到另一个线程。

这要求你的运行时具有专用于卸载阻塞调用的机制(例如线程池)。

我还提出了一些问题,试图防止其他人陷入这个陷阱:

结语

希望该博客能够阐明有关阻塞调用如何与Rust的并发模型进行交互的一些信息!随时提供反馈给我。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
用户文件夹损坏,如何修复?
是山河呀
2025/03/22
2310
如何修复损坏的注册表项?
是山河呀
2025/03/09
1.1K0
如何修复损坏的系统文件?
是山河呀
2025/03/08
5820
固态硬盘损坏 数据恢复_固态硬盘如何恢复数据
先在没有专业技术和维修经验的前提下,切勿自己动手去修复硬盘,操作不当极易对硬盘固件造成二次损坏,带来数据丢失的严重后果!有送修硬盘的客户,由于在小电脑店或者小作坊修过硬盘,失败后才拿到正规渠道做数据恢复,结果还是失败,正是因为一些不正确的操作对受损硬盘进行了二次或者多次损坏,导致数据恢复提取失败!所以,遇到硬盘故障问题,一定要送修专业机构进行数据恢复。
全栈程序员站长
2022/11/05
4.4K0
固态硬盘损坏 数据恢复_固态硬盘如何恢复数据
qq打不开显示0xc0000005_0xc0000001怎么解决
  电脑出现网络不畅的问题很让人头疼,今天尝试了好几种方法,最终终于解决,特此进行记录。如果时间有限,可以直接使用方法3(导入注册表)。
全栈程序员站长
2022/11/09
8730
AutoCAD报错acetransmit.dll怎么办?dll丢失解决方法
acetransmit.dll是一个动态链接库文件,通常与AutoCAD软件相关联。动态链接库(DLL)文件是包含代码和数据的文件,多个程序可以同时使用这些代码和数据。acetransmit.dll文件在AutoCAD中可能用于处理特定的功能或任务,如数据传输、文件转换或其他与AutoCAD操作相关的功能。由于DLL文件的共享特性,它们可以帮助减少程序的内存占用并提高系统的效率。然而,如果acetransmit.dll文件丢失或损坏,可能会导致AutoCAD无法正常运行或出现错误提示。
用户11308197
2024/11/08
970
AutoCAD报错acetransmit.dll怎么办?dll丢失解决方法
电脑开不了机的原因和解决办法
不论是笔记本还是台机 有的时候电脑不明原因开不了机 经过一段时间的总结 看看下面的论述 或许对你有所帮助~~ 开机进入BIOS可能是BIOS电池快没有电了,如果是,更换BIOS电池。
点云PCL博主
2019/07/30
2.6K0
windows错误恢复如何解决_0xc0000006是什么错误
根据出现错误0xc0000005的上下文,可能有多种原因。例如,硬件可能无法正常工作或配置不正确。或者,该错误可能是由于执行的软件引起的,这意味着可以通过重新安装来解决此问题。但是,在大多数情况下,此问题可归因于特定的错误或对操作系统的损坏。在此,误差的范围可以包括错误的或无效的注册表项,缺失或不完整的DLL文件(d ynamic大号墨大号ibrary)或损坏的系统文件和配置。此外,恶意软件 可能是造成“ 0xc0000005”消息的原因。
全栈程序员站长
2022/11/09
5K0
windows错误恢复如何解决_0xc0000006是什么错误
电脑ping命令显示不是内部命令_cmd一直ping的命令
在cmd中用PING命令时,出现’Ping’ 不是内部或外部命令,也不是可运行的程序或批处理文件。
全栈程序员站长
2022/09/29
2.5K0
iPhone突然黑屏开不了机怎么办?3种解决方法分享
当iPhone手机系统遇到严重问题时,iPhone屏幕会变黑,即使长按电源键也无法开机。不用担心。本文将介绍3种方法帮助您解决iPhone黑屏死机的问题。
莉莉的碎碎念
2021/08/02
8.8K0
iPhone突然黑屏开不了机怎么办?3种解决方法分享
SD卡突然损坏怎么办?
在如今的数字时代,存储设备早已成为我们日常生活里不可或缺的好帮手。不管是智能手机、相机,还是电脑、平板,都离不开存储设备来存放我们那些珍贵的照片、视频、重要的文档以及各种各样的数据。不过呢,存储设备也并非坚不可摧,就拿常见的SD卡来说,它虽然体积小巧,便于携带和使用,但在实际使用过程中,还是很可能会碰到各种各样的问题,甚至导致数据丢失或者设备损坏。那么当SD卡突然损坏时,我们究竟该如何应对呢?
用户7704932
2025/03/21
1340
SD卡突然损坏怎么办?
电脑技巧:如何将Win11的右键菜单恢复为Win10的经典风格
随着Windows 11的发布,许多用户对其现代化的界面设计表示欢迎,但也有部分用户怀念Windows 10中更为简洁的传统右键菜单风格。如果你是其中之一,不必担心,本文将指导你如何通过简单的步骤,将Windows 11的右键菜单修改为类似于Windows 10的样式,让你的工作环境更加符合个人偏好。
小明互联网技术分享社区
2024/07/04
53.3K2
电脑技巧:如何将Win11的右键菜单恢复为Win10的经典风格
如何手动修复DLL丢失?dll文件丢失怎么恢复?教你多种方法修复directx缺失!
在Windows操作系统中,DLL(动态链接库)文件扮演着至关重要的角色。它们是包含可被多个程序同时使用的代码和数据的集合体,是系统正常运行不可或缺的组成部分。然而,有时我们可能会遇到DLL文件丢失或损坏的情况,导致程序无法正常运行或系统出现错误。
科技测评师001
2024/08/01
3930
如何手动修复DLL丢失?dll文件丢失怎么恢复?教你多种方法修复directx缺失!
硬盘分区打不开如何修复?硬盘数据恢复工具(带教程)
硬盘分区的目的是把一块单独的物理硬盘,划分为几个各自相对独立的区域,这有助于我们更灵活地管理硬盘,避免未来可能的麻烦。
八碗米饭
2025/03/24
2700
硬盘分区打不开如何修复?硬盘数据恢复工具(带教程)
win7纯净版 摄像头未能创建视频预览怎么办
有时候想打开电脑摄像头,跟远方的亲朋好友视频聊天,却发现系统提示说“未能创建视频预览”,出现这个问题的原因有很多种,应该先判断一下是哪一种原因引起的,怎么解决?下面,小编给大家带来了摄像头未能创建视频预览的处理图文。
用户8052652
2021/01/21
9500
激活windows10转到电脑设置的水印消失3种方法总结
我是企业版,我使用第二种方法激活的window 10! 方法一1、 目前针对Win10正式版的永久激活密钥并没有放出,因此如果想享受永久激活服务,则可以通过重复激活180天服务来持续使用正版Win10系统。 2、在此与大家分享一下Win10正式版VOL(批量激活)密钥。 Win10正式专业版密钥(32位和64位均可正常使用): W269N-WFGWX-YVC9B-4J6C9-T83GX Win10正式企业版密钥(32位和64位均可正常使用): NPPR9-FWDCX-D2C8J-H872
学到老
2018/04/02
4K0
记一次蓝屏日志
虽然说,我是一个在职两年半的程序员,但是对于这个问题其实也和大部分人一样,一脸懵逼🤖
Java_慈祥
2024/08/06
1970
记一次蓝屏日志
你的数据库真的清除干净了嘛?看完这篇文章你会若有所思!!
➖ 彻底清除 MySQL 👨‍🎓作者:Java学术趴 🏦仓库:Github、Gitee ✏️博客:CSDN、掘金、InfoQ、云+社区 💌公众号:Java学术趴 🚫特别声明:原创不易,未经授权不得转载或抄袭,如需转载可联系小编授权。 🙏版权声明:文章里的部分文字或者图片来自于互联网以及百度百科,如有侵权请尽快联系小编。微信搜索公众号Java学术趴联系小编。 ☠️每日毒鸡汤:圈子不同,不必硬融。 👋大家好!我是你们的老朋友java学术趴,今天继续给大家分享你不得不知道的小知识。 数据库,简而言之可视为电子化的
Java学术趴
2021/08/21
4570
Oracle通过ODBC连接SQL Server数据库
近期在项目中客户软件升级,旧版本的数据库用的SQL Server而新版本换为了Oracle,其中部分数据需要进来平移,这样我们就需要配置Oracle连接SQL数据库,这篇我们就来看一下Oracle怎么用ODBC的方式来连接SQL Server数据库。
Vaccae
2019/07/25
9.5K0
【MySQL】下载安装以及SQL介绍
以前我们做系统,数据持久化的存储采用的是文件存储。存储到文件中可以达到系统关闭数据不会丢失的效果,当然文件存储也有它的弊端。
陶然同学
2023/10/14
3190
【MySQL】下载安装以及SQL介绍
推荐阅读
相关推荐
用户文件夹损坏,如何修复?
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验