原文标题:Async/Await


原文链接:https://os.phil-opp.com/async-await/#multitasking

公众号: Rust 碎碎念


翻译 by: Praying

Executors and Wakers

使用 async/await,可以让我们以一种全完异步的方式来与 future 进行更为自然地协作。然而,正如我们之前所了解到的,future 在被轮询之前什么事也不会做。这意味着我们必须在某个时间点上调用poll,否则异步的代码永远都不会执行。

对于单个的 future,我们总是通过使用一个循环手动地等待每个 future。但这种方式十分地低效,且对于一个创建大量 future 的程序来讲也不适用。针对这个问题的最常见的解决方式是定义一个全局的executor

Executors(执行器)

executor 的作用在于能够产生 future 作为独立的任务,通常是通过某种spawn方法。接着 executor 负责轮询所有的 future 直到它们完成。集中管理所有的 future 的巨大优势在于,只要当一个 future 返回Poll::Pending时,executor 就可以切换到另一个 future。因此,异步操作可以并行执行并且 CPU 始终保存繁忙。

许多 executor 的实现充分利用 CPU 多核心的优势,它们创建了一个线程池[1],该线程池能够在工作足够多的情况下充分利用所有的核心,并且使用类似work stealing[2]的方式在核心之间进行负载均衡。还有针对嵌入式系统优化了低延迟和内存负载的特殊的 executor 实现。

为了避免重复轮询 future 的负担,executor 通常会充分利用由 Rust 的 future 支持的 waker API。

Waker

Waker API 背后的设计理念是,一个特定的Waker[3]类型,包装在Context类型中,被传递到poll的每一次执行。这个Waker类型由 executor 创建,并且可以被异步任务用来通知自己的完成。因此,executor 不需要在一个 future 返回Poll::Pending之前对其调用poll,直到它被对应的 waker 调用。

这可以通过一个小例子来阐述:

async fn write_file() {
    async_write_file("foo.txt", "Hello").await;
}

这个函数异步地把一个字符串“Hello”写入到文件foo.txt中。因为硬盘写入需要一点儿时间,所以 future 上的第一次poll调用很大可能返回Poll::Pending。尽管如此,硬盘驱动将把传递给poll调用的Waker存储起来,并在当文件被完全写入磁盘后使用它来提醒 executor,通过这种方式,executor 在收到 waker 提醒之前不需要浪费时间一次又一次地去轮询这个 future。

当我们在后面的章节实现自己的支持 waker 的 executor 时,我们就会看到Waker类型更详细的工作原理。

协作式多任务?

在本文(系列)开头,我们讨论了抢占式和协作式多任务。抢占式多任务依赖于操作系统在运行中的任务间进行强制切换,协作式多任务则需要任务通过一个yield操作自愿放弃对 CPU 的控制权。协作式多任务的巨大优势在于,任务可以自己保存自身状态,从而产生更为高效的上下文切换,并使得在任务间共享相同的调用栈成为可能。

虽然看上去可能不太明显,但是 future 和 async/await 是一种协作式多任务模式的实现:

  • 每个被添加到 executor 的 future 是一个协作式任务。

  • 不同于显式的yield操作,future 通过返回Poll::Pending(或者是结束时的Poll::Ready)来放弃对 CPU 核心的控制权。

    • 没有什么可以强制让 future 放弃 CPU,future 可以永远不从poll里面返回,例如,无限循环。

    • 因为每个 future 都能阻塞 executor 中其他 future 的执行,所以我们需要确信它们不是恶意的。

  • Futures 内部存储了需要在下次poll调用继续执行所需的所有状态。通过 async/await,编译器会自动探测所有需要的变量并将其存储在生成的状态机内部。

    • 只保存继续执行需要的最小状态
    • 因为poll方法在返回时放弃了调用栈,所以同一个栈可以被用于轮询其他的 future。

我们可以看到,future 和 async/await 完美契合协作式多任务模式,它们只是用了一些不同的技术。接下来,我们将会交替使用“任务(task)”和“future”。

参考资料

[1]

线程池: https://en.wikipedia.org/wiki/Thread_pool

[2]

work stealing: https://en.wikipedia.org/wiki/Work_stealing

[3]

Waker: https://doc.rust-lang.org/nightly/core/task/struct.Waker.html

```

【译】Async/Await(五)—— Executors and Wakers的更多相关文章

  1. [译]async/await中使用阻塞式代码导致死锁 百万数据排序:优化的选择排序(堆排序)

    [译]async/await中使用阻塞式代码导致死锁 这篇博文主要是讲解在async/await中使用阻塞式代码导致死锁的问题,以及如何避免出现这种死锁.内容主要是从作者Stephen Cleary的 ...

  2. [译]async/await中使用阻塞式代码导致死锁

    原文:[译]async/await中使用阻塞式代码导致死锁 这篇博文主要是讲解在async/await中使用阻塞式代码导致死锁的问题,以及如何避免出现这种死锁.内容主要是从作者Stephen Clea ...

  3. [译]async/await中阻塞死锁

    这篇博文主要是讲解在async/await中使用阻塞式代码导致死锁的问题,以及如何避免出现这种死锁.内容主要是从作者Stephen Cleary的两篇博文中翻译过来. 原文1:Don'tBlock o ...

  4. [译]Async/Await - Best Practices in Asynchronous Programming

    原文 避免async void async void异步方法只有一个目的:使得event handler异步可行,也就是说async void只能用于event handler. async void ...

  5. 【译】异步JavaScript的演变史:从回调到Promises再到Async/Await

    我最喜欢的网站之一是BerkshireHathaway.com--它简单,有效,并且自1997年推出以来一直正常运行.更值得注意的是,在过去的20年中,这个网站很有可能从未出现过错误.为什么?因为它都 ...

  6. 聊聊多线程那一些事儿 之 五 async.await深度剖析

     hello task,咱们又见面啦!!是不是觉得很熟读的开场白,哈哈你哟这感觉那就对了,说明你已经阅读过了我总结的前面4篇关于task的文章,谢谢支持!感觉不熟悉的也没有关系,在文章末尾我会列出前四 ...

  7. 【译】Async/Await(一)——多任务

    原文标题:Async/Await 原文链接:https://os.phil-opp.com/async-await/#multitasking 公众号: Rust 碎碎念 翻译 by: Praying ...

  8. 【译】Async/Await(二)——Futures

    原文标题:Async/Await 原文链接:https://os.phil-opp.com/async-await/#multitasking 公众号: Rust 碎碎念 翻译 by: Praying ...

  9. 【译】Async/Await(三)——Aysnc/Await模式

    原文标题:Async/Await 原文链接:https://os.phil-opp.com/async-await/#multitasking 公众号: Rust 碎碎念 翻译 by: Praying ...

随机推荐

  1. 安装MySQL数据库(在Windows下通过zip压缩包安装)

    安装MySQL 这里建议大家使用压缩版,安装快,方便.不复杂. 软件下载 mysql5.7 64位下载地址: https://dev.mysql.com/get/Downloads/MySQL-5.7 ...

  2. 【Oracle】生成随机数

    Oracle生成随机数: dbms_random.string(opt, 6)     --括号里的opt要从下面的列表中选择,数字代表要生成几位随机数,如果是1位的话,就改成1 以此类推 opt可取 ...

  3. Pandas应用案例-股票分析:使用tushare包获取股票的历史行情数据进行数据分析

    目标: 使用tushare包获取股票的历史行情数据 输出该股票所有收盘比开盘上涨3%以上的日期 输出该股票所有开盘比前日收盘跌幅超过2%以上的日期 假如为我们从2010年1月1日开始,每月第一个交易日 ...

  4. online创建索引中途取消导致索引无法删除解决办法

    问题:有一个表ID栏位没有索引,但是在一个update语句的where中被使用,因此打算online创建索引,但是长时间没有成功,此时决定取消,取消后发现索引无法删除 过程: 数据库监控报警有行锁,进 ...

  5. 【JeecgBoot】关于 jeecg-boot 的项目理解、使用心得和改进建议

    工欲善其事,必先利其器. 脚手架选型 一年前,我接到为团队落地一个快速开发脚手架的任务. 在月底这节骨眼上,时间紧,任务急,有想自己撸一个脚手架的人都赶紧把这想法收起来吧!这劳民又伤身的事咱肯定是不能 ...

  6. 如何创建一个Java项目

    目录 新建项目 项目信息配置 创建Java类 编译和运行 新建项目 首先双击eclipse进入到eclipse页面. 菜单"File"下的"New"里" ...

  7. 前端面试之HTTP状态码!

    前端面试之HTTP协议的东西! 一次HTTP请求的流程! HTTP 状态码 成功响应(200–299) 状态码 含义 200 请求成功 201 该请求已成功,并因此创建了一个新的资源.这通常是在POS ...

  8. WinForm中实现按Enter将光标移动到下一个文本框

    首先窗体加载出来是上面这个样子.有五个文本框,我们要实现的功能就是输入姓名后按Enter,使光标直接定位到手机号中. 在页面加载的时候我们就要获取所有文本框控件,并添加回车事件 private voi ...

  9. Soul API 网关源码解析 02

    如何读开源项目:对着文档跑demo,对着demo看代码,懂一点就开始试,有问题了问社区. 今日目标: 1.运行examples下面的 http服务 2.学习文档,结合divde插件,发起http请求s ...

  10. MySQL调优用户监控之show processlist

    简介 show processlist显示这台MySQL正在连接的用户: mysql> show processlist; +----+------+-----------+-------+-- ...