Skip to content

Commit

Permalink
Merge pull request #95 from huangjj27/gitlocalize-27511
Browse files Browse the repository at this point in the history
年度同步翻译更新上游文档
  • Loading branch information
huangjj27 authored Apr 23, 2024
2 parents 915baaa + 175d0fd commit 868dc0e
Show file tree
Hide file tree
Showing 9 changed files with 60 additions and 18 deletions.
4 changes: 3 additions & 1 deletion src_zh-CN/02_execution/02_future.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@ Futures的这种模型允许组合多个异步操作而无需立刻分配资源
{{#include ../../examples/02_02_future_trait/src/lib.rs:real_future}}
```

我们首先注意到 `self` 参数类型不再是 `mut self` 而是 `Pin<&mut Self>,`。我们会在后面章节 更多地讨论固定(pinning)的问题,但现在我们只需要知道它能让我们创建不可移动的future类型。 不可移动对象能够储存指向另一字段(field)的指针,例如:`struct MyFut { a: i32, ptr_to_a: *const i32 }`。固定对于启动 async/await 是必需的。
我们首先注意到 `self` 参数类型不再是 `&mut self` 而是 `Pin<&mut Self>,`。我们会在后面章节 更多地讨论固定(pinning)的问题,但现在我们只需要知道它能让我们创建不可移动的future类型。 不可移动对象能够储存指向另一字段(field)的指针,例如:`struct MyFut { a: i32, ptr_to_a: *const i32 }`。固定对于启动 async/await 是必需的。

然后 `wake: fn()` 变成了 `&mut Context<'_>`。在 `SimpleFuture` 里,我们调用函数指针(`fn()`) 来告诉执行器有future需要轮询。然而,因为 `fn()` 是仅仅是个函数指针,它不能储存任何信息说明哪个 `Future` 调用了 `wake`

在现实场景中,像Web服务器这样复杂的应用可能有上千不同的连接,带有应该相互隔离来管理的 唤醒器(wakeups)。`Context` 类型通过提供对 `waker` 类型的访问来解决这个问题,这些 `waker` 会唤起持定任务。


20 changes: 10 additions & 10 deletions src_zh-CN/02_execution/03_wakeups.md
Original file line number Diff line number Diff line change
@@ -1,38 +1,38 @@
# `Waker`唤醒任务
# `Waker` 唤醒任务

future第一次轮询时没有执行完这事很常见。此时,future需要保证会被再次轮询以进展(make progress),而这由`Waker`类型负责。
future 第一次轮询时没有执行完这事很常见。此时,future需要保证会被再次轮询以进展(make progress),而这由 `Waker` 类型负责。

每次future被轮询时, 它是作为一个“任务”的一部分轮询的。任务(Task)是能提交到执行器上 的顶层future
每次 future 被轮询时, 它是作为一个“任务”的一部分轮询的。任务(Task)是能提交到执行器上的顶层 future

`Waker`提供`wake()`方法来告诉执行器哪个关联任务应该要唤醒。当`wake()`函数被调用时, 执行器知道`Waker`关联的任务已经准备好继续了,并且任务的future会被轮询一遍
`Waker` 提供 `wake()` 方法来告诉执行器哪个关联任务应该要唤醒。当 `wake()` 函数被调用时, 执行器知道 `Waker` 关联的任务已经准备好继续了,并且任务的 future 会被轮询一遍

`Waker`类型还实现了`clone()`,因此可以到处拷贝储存。
`Waker` 类型还实现了 `clone()`,因此可以到处拷贝储存。

我们来试试用`Waker`实现一个简单的计时器future吧
我们来试试用 `Waker` 实现一个简单的计时器 future 吧

## 应用:构建计时器

这个例子的目标是: 在创建计时器时创建新线程,休眠特定时间,然后过了时间窗口时通知(signal) 计时器future。

首先,用`cargo new --lib timer_future` 命令来新建项目,并加入我们需要用来编写 `src/lib.rs` 的依赖:
首先,用 `cargo new --lib timer_future` 命令来新建项目,并加入我们需要用来编写 `src/lib.rs` 的依赖:

```rust
{{#include ../../examples/02_03_timer/src/lib.rs:imports}}
```

我们开始定义future类型吧。 我们的future需要一个方法,让线程知道计时器倒数完了,future 应该要完成了。我们准备用`Arc<Mutex<..>>`共享值来为沟通线程和future
我们开始定义 future 类型吧。 我们的 future 需要一个方法,让线程知道计时器倒数完了,future 应该要完成了。我们准备用 `Arc<Mutex<..>>` 共享值来为沟通线程和 future

```rust,ignore
{{#include ../../examples/02_03_timer/src/lib.rs:timer_decl}}
```

现在,我们来实现`Future`吧!
现在,我们来实现 `Future` 吧!

```rust,ignore
{{#include ../../examples/02_03_timer/src/lib.rs:future_for_timer}}
```

很简单,对吧?如果线程已经设置成`shared_state.completed = true`,我们就搞定了!否则, 我们从当前任务克隆`Waker`并把它传到`shared_state.waker`,这样线程就能回头再唤醒这个任务。
很简单,对吧?如果线程已经设置成 `shared_state.completed = true`,我们就搞定了!否则, 我们从当前任务克隆 `Waker` 并把它传到 `shared_state.waker`,这样线程就能回头再唤醒这个任务。

重要的是,每次future轮询后,我们必须更新`Waker`,这是因为这个future可能会移动到不同的 任务去,带着不同的`Waker`。这会在future轮询后在不同任务间移动时发生。

Expand Down
2 changes: 1 addition & 1 deletion src_zh-CN/02_execution/04_executor.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Rust 的 `Future` 是惰性的:它们不会干任何事,除非它们被驱
name = "timer_future"
version = "0.1.0"
authors = ["XYZ Author"]
edition = "2018"
edition = "2021"

[dependencies]
futures = "0.3"
Expand Down
2 changes: 1 addition & 1 deletion src_zh-CN/03_async_await/01_chapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,4 @@
类似的,横跨 `.await` 持有一个非 future 感知的锁这种做法是很不好的,因为它能导致整个线程池 锁上:一个任务可能获得了锁,`.await` 然后让出到执行器,允许其他任务尝试获取所并导致死锁。 为了避免这种情况,使用 `futures::lock`里的 `Mutex` 类型比起 `std::sync` 里面的更好。


[第一章]: ../01_getting_started/04_async_await_primer.md
[第一章]: ../01_getting_started/04_async_await_primer.md
28 changes: 25 additions & 3 deletions src_zh-CN/04_pinning/01_chapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ enum State {
}
impl Future for AsyncFuture {
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
loop {
match self.state {
State::AwaitingFutOne => match self.fut_one.poll(..) {
Expand Down Expand Up @@ -444,7 +446,27 @@ pub fn main() {
# }
```

类型系统会阻止我们移动这些数据。
类型系统会阻止我们移动这些数据,像下面这样:

```
error[E0277]: `PhantomPinned` cannot be unpinned
--> src\test.rs:56:30
|
56 | std::mem::swap(test1.get_mut(), test2.get_mut());
| ^^^^^^^ within `test1::Test`, the trait `Unpin` is not implemented for `PhantomPinned`
|
= note: consider using `Box::pin`
note: required because it appears within the type `test1::Test`
--> src\test.rs:7:8
|
7 | struct Test {
| ^^^^
note: required by a bound in `std::pin::Pin::<&'a mut T>::get_mut`
--> <...>rustlib/src/rust\library\core\src\pin.rs:748:12
|
748 | T: Unpin,
| ^^^^^ required by this bound in `std::pin::Pin::<&'a mut T>::get_mut`
```

> 重点记住,固定到栈总是依赖你在写 `unsafe` 代码时提供的保证。例如,我们知道了 `&'a mut T`*被指向对象(pointee)* 在生命周期 `'a` 期间固定,我们不知道被 `&'a mut T` 指向数据是否在 `'a` 结束后仍然不被移动。如果移动了,将会违反固定的协约。
>
Expand Down Expand Up @@ -594,4 +616,4 @@ execute_unpin_future(fut); // OK

[执行 `Future` 与任务]: ../02_execution/01_chapter.md
[`Future` trait]: ../02_execution/02_future.md
[`pin_utils`]: https://docs.rs/pin-utils/
[`pin_utils`]: https://docs.rs/pin-utils/
17 changes: 17 additions & 0 deletions src_zh-CN/06_multiple_futures/04_spawning.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# `分派`

分派(Spawning)允许你在后台运行新的异步任务(task)。这允许我们在该任务运行时执行其他代码。

例如我们又一台服务器,想要它接受新连接时又不阻塞住线程。为此,我们可以用 `async_std::task::spawn` 函数来创建并运行一个新任务,来处理链接。这个函数输入一个 future,并且返回一个 `JoinHandle` 来等待这个任务(一旦它完成)的结果。

```rust,edition2018
{{#include ../../examples/06_04_spawning/src/lib.rs:example}}
```

`spawn` 返回的 `JoinHandle` 实现了 `Future` trait, 所以我们可以 `.await` 来获取任务的结果。这会阻塞当前任务,直到分派的任务完成。如果任务没有被await,你的程序会继续执行而不等待该任务,该任务也会在函数比任务先完成时被取消。

```rust,edition2018
{{#include ../../examples/06_04_spawning/src/lib.rs:join_all}}
```

为了在主任务与分派的任务间沟通,我们可以使用异步运行时提供的通道(channels)。
1 change: 0 additions & 1 deletion src_zh-CN/09_example/03_tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
首先,我们要更改 `handle_connection` 的签名,来使得它更容易测试。`handle_connection` 其实并不需要 `async_std::net::TcpStream`,它需要的是任意已经实现了 `async_std::io::Read`, `async_std::io::Write``marker::Unpin`。这样修改类型签名允许我们传递一个 mock 来测试。

```rust,ignore
use std::marker::Unpin;
use async_std::io::{Read, Write};
async fn handle_connection(mut stream: impl Read + Write + Unpin) {
Expand Down
1 change: 1 addition & 0 deletions src_zh-CN/12_appendix/01_translations.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@

- [俄语](https://doc.rust-lang.ru/async-book/)
- [法语](https://jimskapt.github.io/async-book-fr/)
- [فارسی](https://rouzbehsbz.github.io/rust-async-book/)
3 changes: 2 additions & 1 deletion src_zh-CN/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
- [同时执行多个 Future](06_multiple_futures/01_chapter.md)
- [`join!`](06_multiple_futures/02_join.md)
- [`select!`](06_multiple_futures/03_select.md)
- [TODO: Spawning]()
- [分派(任务)](06_multiple_futures/04_spawning.md)
- [TODO: Cancellation and Timeouts]()
- [TODO: `FuturesUnordered`]()
- [需要知道并热爱的规避方法](07_workarounds/01_chapter.md)
Expand All @@ -34,3 +34,4 @@
- [TODO: Asynchronous Design Patterns: Solutions and Suggestions]()
- [TODO: Modeling Servers and the Request/Response Pattern]()
- [TODO: Managing Shared State]()
- [附录: 本书翻译](12_appendix/01_translations.md)

0 comments on commit 868dc0e

Please sign in to comment.