【翻译】200行代码讲透RUST FUTURES (3)

原创
2020/07/26 22:59
阅读数 91

三 Rust中的Futures

概述

  1. Rust中并发性的高级介绍

  2. 了解 Rust 在使用异步代码时能提供什么,不能提供什么

  3. 了解为什么我们需要 Rust 的运行时库

  4. 理解“leaf-future”和“non-leaf-future”的区别

  5. 了解如何处理 CPU 密集型任务

Futures

什么是Future?
Future是一些将在未来完成的操作。
Rust中的异步实现基于轮询,每个异步任务分成三个阶段:

  1. 轮询阶段(The Poll phase). 一个Future被轮询后,会开始执行,直到被阻塞. 我们经常把轮询一个Future这部分称之为执行器(executor)

  2. 等待阶段. 事件源(通常称为reactor)注册等待一个事件发生,并确保当该事件准备好时唤醒相应的Future

  3. 唤醒阶段. 事件发生,相应的Future被唤醒。现在轮到执行器(executor),就是第一步中的那个执行器,调度Future再次被轮询,并向前走一步,直到它完成或达到一个阻塞点,不能再向前走, 如此往复,直到最终完成.

当我们谈论Future的时候,我发现在早期区分non-leaf-futureleaf-future是很有用的,因为实际上它们彼此很不一样。

Leaf futures

由运行时创建leaf futures,它就像套接字一样,代表着一种资源.

// stream is a **leaf-future**
let mut stream = tokio::net::TcpStream::connect("127.0.0.1:3000");

对这些资源的操作,比如套接字上的 Read 操作,将是非阻塞的,并返回一个我们称之为leaf-future的Future.之所以称之为leaf-future,是因为这是我们实际上正在等待的Future.

除非你正在编写一个运行时,否则你不太可能自己实现一个leaf-future,但是我们将在本书中详细介绍它们是如何构造的。

您也不太可能将 leaf-future 传递给运行时,然后单独运行它直到完成,这一点您可以通过阅读下一段来理解。

Non-leaf-futures

Non-leaf-futures指的是那些我们用async关键字创建的Future.

异步程序的大部分是Non-leaf-futures,这是一种可暂停的计算。这是一个重要的区别,因为这些Future代表一组操作。通常,这样的任务由await 一系列leaf-future组成.

// Non-leaf-future
let non_leaf = async {
let mut stream = TcpStream::connect("127.0.0.1:3000").await.unwrap();// <- yield
println!("connected!");
let result = stream.write(b"hello world\n").await; // <- yield
println!("message sent!");
...
};

这些任务的关键是,它们能够将控制权交给运行时的调度程序,然后在稍后停止的地方继续执行。

leaf-future相比,这些Future本身并不代表I/O资源。当我们对这些Future进行轮询时, 有可能会运行一段时间或者因为等待相关资源而让度给调度器,然后等待相关资源ready的时候唤醒自己.

运行时(Runtimes)

像 c # ,JavaScript,Java,GO 和许多其他语言都有一个处理并发的运行时。所以如果你来自这些语言中的一种,这对你来说可能会有点奇怪。

Rust 与这些语言的不同之处在于 Rust 没有处理并发性的运行时,因此您需要使用一个为您提供此功能的库。

很多复杂性归因于 Futures 实际上是来源于运行时的复杂性,创建一个有效的运行时是困难的。
学习如何正确使用一个也需要相当多的努力,但是你会看到这些类型的运行时之间有几个相似之处,所以学习一个可以使学习下一个更容易。

Rust 和其他语言的区别在于,在选择运行时时,您必须进行主动选择。大多数情况下,在其他语言中,你只会使用提供给你的那一种。

异步运行时可以分为两部分:

  1. 执行器(The Executor)

  2. reactor (The Reactor)

当 Rusts Futures 被设计出来的时候,有一个愿望,那就是将通知Future它可以做更多工作的工作与Future实际做工作分开。

你可以认为前者是reactor的工作,后者是执行器的工作。运行时的这两个部分使用 Waker进行交互。

写这篇文章的时候,未来最受欢迎的两个运行时是:

  1. async-std

  2. Tokio

Rust 的标准库做了什么

  1. 一个公共接口,Future trait

  2. 一个符合人体工程学的方法创建任务, 可以通过async和await关键字进行暂停和恢复Future

  3. Waker接口, 可以唤醒暂停的Future

这就是Rust标准库所做的。正如你所看到的,不包括异步I/O的定义,这些异步任务是如何被创建的,如何运行的。

I/O密集型 VS CPU密集型任务

正如你们现在所知道的,你们通常所写的是Non-leaf-futures。让我们以 pseudo-rust 为例来看一下这个异步块:

let non_leaf = async {
let mut stream = TcpStream::connect("127.0.0.1:3000").await.unwrap(); // <-- yield

// request a large dataset
let result = stream.write(get_dataset_request).await.unwrap(); // <-- yield

// wait for the dataset
let mut response = vec![];
stream.read(&mut response).await.unwrap(); // <-- yield

// do some CPU-intensive analysis on the dataset
let report = analyzer::analyze_data(response).unwrap();

// send the results back
stream.write(report).await.unwrap(); // <-- yield
};

现在,正如您将看到的,当我们介绍 Futures 的工作原理时,两个yield之间的代码与我们的执行器在同一个线程上运行。

这意味着当我们分析器处理数据集时,执行器忙于计算而不是处理新的请求。

幸运的是,有几种方法可以解决这个问题,这并不困难,但是你必须意识到:

  1. 我们可以创建一个新的leaf future,它将我们的任务发送到另一个线程,并在任务完成时解析。我们可以像等待其他Future一样等待这个leaf-future

  2. 运行时可以有某种类型的管理程序来监视不同的任务占用多少时间,并将执行器本身移动到不同的线程,这样即使我们的分析程序任务阻塞了原始的执行程序线程,它也可以继续运行。

  3. 您可以自己创建一个与运行时兼容的reactor,以您认为合适的任何方式进行分析,并返回一个可以等待的未来。

现在,#1是通常的处理方式,但是一些执行器也实现了#2。2的问题是,如果你切换运行时,你需要确保它也支持这种监督,否则你最终会阻塞执行者。

方式#3更多的是理论上的重要性,通常您会很乐意将任务发送到多数运行时提供的线程池。

大多数执行器都可以使用诸如 spawn blocking 之类的方法来完成#1。

这些方法将任务发送到运行时创建的线程池,在该线程池中,您可以执行 cpu 密集型任务,也可以执行运行时不支持的“阻塞”任务。

现在,有了这些知识,你已经在一个很好的方式来理解Future,但我们不会停止,有很多细节需要讨论。

休息一下或喝杯咖啡,准备好我们进入下一章的深度探索。

奖励部分

如果你发现并发和异步编程的概念一般来说令人困惑,我知道你是从哪里来的,我已经写了一些资源,试图给出一个高层次的概述,这将使之后更容易学习 Rusts Futures:

  • Async Basics - The difference between concurrency and parallelism 异步基础-并发和并行之间的区别

  • Async Basics - Async history 异步基础-异步历史

  • Async Basics - Strategies for handling I/O 异步基础-处理 i / o 的策略

  • Async Basics - Epoll, Kqueue and IOCP 异步基础-Epoll,Kqueue 和 IOCP

通过研究Future来学习这些概念会让它变得比实际需要难得多,所以如果你有点不确定的话,继续读这些章节。

你回来的时候我就在这儿。

如果你觉得你已经掌握了基本知识,那么让我们开始行动吧!


本文分享自微信公众号 - Rust语言中文社区(rust-china)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部