|
|
@ -247,7 +247,7 @@ received 'you'
|
|
|
|
|
|
|
|
|
|
|
|
</figure>
|
|
|
|
</figure>
|
|
|
|
|
|
|
|
|
|
|
|
我们可以使用 `trpl::join!` 来 await 它们,因为它允许你传递多个 future 类型并产生一个这些类型的元组。我们 *不能* 使用 `trpl::join_all`,因为它要求传递的 future 都拥有相同的类型。请记住,那个错误正式我们开启 `Pin` 探索之旅的原因!
|
|
|
|
我们可以使用 `trpl::join!` 来 await 它们,因为它允许你传递多个 future 类型并产生一个这些类型的元组。我们 *不能* 使用 `trpl::join_all`,因为它要求传递的 future 都拥有相同的类型。请记住,那个错误正是我们开启 `Pin` 探索之旅的原因!
|
|
|
|
|
|
|
|
|
|
|
|
这是一个基础的权衡取舍:要么我们可以使用 `join_all` 处理动态数量的 future,只要它们都有相同的类型;要么我们可以使用 `join` 函数或者 `join!` 宏来处理固定数量的 future,哪怕它们有着不同的类型。不过这与 Rust 处理任何其它类型是一样的。Future 并不特殊,即便我们采用了一些友好的语法来处理它们,而这其实是好事。
|
|
|
|
这是一个基础的权衡取舍:要么我们可以使用 `join_all` 处理动态数量的 future,只要它们都有相同的类型;要么我们可以使用 `join` 函数或者 `join!` 宏来处理固定数量的 future,哪怕它们有着不同的类型。不过这与 Rust 处理任何其它类型是一样的。Future 并不特殊,即便我们采用了一些友好的语法来处理它们,而这其实是好事。
|
|
|
|
|
|
|
|
|
|
|
@ -271,9 +271,31 @@ received 'you'
|
|
|
|
|
|
|
|
|
|
|
|
请注意如果你反转 `race` 参数的顺序,“started” 消息的顺序会改变,即使 `fast` future 总是第一个结束。这是因为这个特定的 `race` 函数实现并不是公平的。它总是以传递的参数的顺序来运行传递的 futures。其它的实现 *是* 公平的,并且会随机选择首先轮询的 future。不过无论我们使用的 race 实现是否公平,其中 *一个* future 会在另一个任务开始之前一直运行到异步代码块中第一个 `await` 为止。
|
|
|
|
请注意如果你反转 `race` 参数的顺序,“started” 消息的顺序会改变,即使 `fast` future 总是第一个结束。这是因为这个特定的 `race` 函数实现并不是公平的。它总是以传递的参数的顺序来运行传递的 futures。其它的实现 *是* 公平的,并且会随机选择首先轮询的 future。不过无论我们使用的 race 实现是否公平,其中 *一个* future 会在另一个任务开始之前一直运行到异步代码块中第一个 `await` 为止。
|
|
|
|
|
|
|
|
|
|
|
|
回忆一下[第一个异步程序][async-program]中提到在每一个 await point,如果被 await 的 future 还没有就绪,Rust 会给运行时一个机会来暂停该任务并切换到另一个。这反过来也是正确的:Rust *只会* 在一个 await point 暂停异步代码块并将控制权交还给运行时。await points 之间的一切都是同步。
|
|
|
|
回忆一下[第一个异步程序][async-program]中提到在每一个 await point,如果被 await 的 future 还没有就绪,Rust 会给运行时一个机会来暂停该任务并切换到另一个任务。反过来也是正确的:Rust *只会* 在一个 await point 暂停异步代码块并将控制权交还给运行时。await points 之间的一切都是同步。
|
|
|
|
|
|
|
|
|
|
|
|
这意味着如果你在异步代码块中做了一堆工作而没有一个 await point,则那个 future 会阻塞其它任何 future 继续进行。
|
|
|
|
这意味着如果你在异步代码块中做了一堆工作而没有一个 await point,则那个 future 会阻塞其它任何 future 继续进行。有时你可能会听说这称为一个 future *starving* 其它 future。在一些情况中,这可能不是什么大问题。不过,如果你在进行某种昂贵的设置或者上时间运行的任务,亦或有一个 future 会无限持续运行某些特定任务的话,你会需要思考在何时何地将控制权交还运行时。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
同样地,如果你有长时间运行的阻塞操作,异步可能是一个提供了将程序的不同部分相互关联起来的实用工具。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
不过在这种情况下 *如何* 将控制权交还运行时呢?
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### Yielding
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
让我们模拟一个长时间运行的操作。示例 17-22 引入了一个 `slow` 函数。它使用 `std::thread::sleep` 而不是 `trpl::sleep` 因此 `slow` 调用会阻塞当前线程若干毫秒。我们可以用 `slow` 来代表现实世界中的长时间运行并阻塞的操作。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<figure class="listing">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<span class="file-name">文件名:src/main.rs</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
|
|
|
{{#rustdoc_include ../listings/ch17-async-await/listing-17-22/src/main.rs:slow}}
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<figcaption>示例 17-22:使用 `thread::sleep` 来模拟缓慢的操作</figcaption>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</figure>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
在示例 17-22 中,我们使用 `slow` 在几个 future 中模拟这类 CPU 密集型工作。首先
|
|
|
|
|
|
|
|
|
|
|
|
[collections]: ch08-01-vectors.html#using-an-enum-to-store-multiple-types
|
|
|
|
[collections]: ch08-01-vectors.html#using-an-enum-to-store-multiple-types
|
|
|
|
[dyn]: ch12-03-improving-error-handling-and-modularity.html
|
|
|
|
[dyn]: ch12-03-improving-error-handling-and-modularity.html
|
|
|
|