原文标题: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. 日常分享:关于时间复杂度和空间复杂度的一些优化心得分享(C#)

    前言 今天分享一下日常工作中遇到的性能问题和解决方案,比较零碎,后续会持续更新(运行环境为.net core 3.1) 本次分享的案例都是由实际生产而来,经过简化后作为举例 Part 1(作为简单数据 ...

  2. Linux安装MYSQL并部署主从复制集群

    主节点部署 安装数据库 Ubuntu apt-get install mysql-server -y systemctl start mysql systemctl enabled mysql Cen ...

  3. libuv中实现tcp服务器

    目录 1.说明 2.libuv的tcp server 3.API简介 3.1.uv_tcp_init 3.2.uv_ip4_addr 3.3.uv_tcp_bind 3.4.uv_listen 3.5 ...

  4. 公共错误码 - 支付宝开放平台 https://opendocs.alipay.com/open/common/105806

    公共错误码 - 支付宝开放平台 https://opendocs.alipay.com/open/common/105806

  5. 两个报文是如何进行 TCP 分组传输

    16 | 如何理解TCP的"流"? https://time.geekbang.org/column/article/132443 TCP 是一种流式协议在前面的章节中,我们讲的都 ...

  6. Feign配置日志的打印级别

    一.细粒度的配置Feign的日志级别(针对每个微服务配置) 1.java代码方式 (1)在Feign接口注解上面配置configuration /** * @author : maybesuch * ...

  7. qbxt 学习笔记 10.2

    写在前面 昨晚网络咕了,而且比较晚,没交作业.解题报告写成书面的了,代码另发 + 博客. 目录 写在前面 1. 爬山算法 概述 示例 2. 模拟退火 概述 3. Meet in the Middle ...

  8. Linux环境Hadoop安装配置

    Linux环境Hadoop安装配置 1. 准备工作 (1)linux配置IP(NAT模式) (2)linux关闭防火墙 (3)设置主机名 (4)设置映射 (5)设置免密登录 2. 安装jdk (1)上 ...

  9. 利用powershell隐藏执行后门

    运行后门时,有些后门不能中断.双击运行这种后门会产生一个黑框. 一条命令就能使其在后台执行 命令解释: start-process启动一个进程 -windowstyle窗口样式 hidden隐藏

  10. cachedThreadPool缓存线程池

    package com.loan.modules.common.util; import java.util.concurrent.BlockingQueue; import java.util.co ...