diff --git a/src/async-rust/async/getting-started.md b/src/async-rust/async/getting-started.md index 03774d24..21c2fa3e 100644 --- a/src/async-rust/async/getting-started.md +++ b/src/async-rust/async/getting-started.md @@ -18,8 +18,8 @@ 由于并发编程在现代社会非常重要,因此每个主流语言都对自己的并发模型进行过权衡取舍和精心设计,Rust 语言也不例外。下面的列表可以帮助大家理解不同并发模型的取舍: -- **OS 线程**, 它最简单,也无需改变任何编程模型(业务/代码逻辑),因此非常适合作为语言的原生并发模型,我们在[多线程章节](https://course.rs/advance/concurrency-with-threads/concurrency-parallelism.html)也提到过,Rust 就选择了原生支持线程级的并发编程。但是,这种模型也有缺点,例如线程间的同步将变得更加困难,线程间的上下文切换损耗较大。使用线程池在一定程度上可以提升性能,但是对于 IO 密集的场景来说,线程池还是不够看。 -- **事件驱动(Event driven)**, 这个名词你可能比较陌生,如果说事件驱动常常跟回调( Callback )一起使用,相信大家就恍然大悟了。这种模型性能相当的好,但最大的问题就是存在回调地狱的风险:非线性的控制流和结果处理导致了数据流向和错误传播变得难以掌控,还会导致代码可维护性和可读性的大幅降低,大名鼎鼎的 JS 曾经就存在回调地狱。 +- **OS 线程**, 它最简单,也无需改变任何编程模型(业务/代码逻辑),因此非常适合作为语言的原生并发模型,我们在[多线程章节](https://course.rs/advance/concurrency-with-threads/concurrency-parallelism.html)也提到过,Rust 就选择了原生支持线程级的并发编程。但是,这种模型也有缺点,例如线程间的同步将变得更加困难,线程间的上下文切换损耗较大。使用线程池在一定程度上可以提升性能,但是对于 IO 密集的场景来说,线程池还是不够。 +- **事件驱动(Event driven)**, 这个名词你可能比较陌生,如果说事件驱动常常跟回调( Callback )一起使用,相信大家就恍然大悟了。这种模型性能相当的好,但最大的问题就是存在回调地狱的风险:非线性的控制流和结果处理导致了数据流向和错误传播变得难以掌控,还会导致代码可维护性和可读性的大幅降低,大名鼎鼎的 `JavaScript` 曾经就存在回调地狱。 - **协程(Coroutines)** 可能是目前最火的并发模型,`Go` 语言的协程设计就非常优秀,这也是 `Go` 语言能够迅速火遍全球的杀手锏之一。协程跟线程类似,无需改变编程模型,同时,它也跟 `async` 类似,可以支持大量的任务并发运行。但协程抽象层次过高,导致用户无法接触到底层的细节,这对于系统编程语言和自定义异步运行时是难以接受的 - **actor 模型**是 erlang 的杀手锏之一,它将所有并发计算分割成一个一个单元,这些单元被称为 `actor` , 单元之间通过消息传递的方式进行通信和数据传递,跟分布式系统的设计理念非常相像。由于 `actor` 模型跟现实很贴近,因此它相对来说更容易实现,但是一旦遇到流控制、失败重试等场景时,就会变得不太好用 - **async/await**, 该模型性能高,还能支持底层编程,同时又像线程和协程那样无需过多的改变编程模型,但有得必有失,`async` 模型的问题就是内部实现机制过于复杂,对于用户来说,理解和使用起来也没有线程和协程简单,好在前者的复杂性开发者们已经帮我们封装好,而理解和使用起来不够简单,正是本章试图解决的问题。 @@ -36,7 +36,7 @@ - **Future 在 Rust 中是惰性的**,只有在被轮询(`poll`)时才会运行, 因此丢弃一个 `future` 会阻止它未来再被运行, 你可以将`Future`理解为一个在未来某个时间点被调度执行的任务。 - **Async 在 Rust 中使用开销是零**, 意味着只有你能看到的代码(自己的代码)才有性能损耗,你看不到的(`async` 内部实现)都没有性能损耗,例如,你可以无需分配任何堆内存、也无需任何动态分发来使用 `async` ,这对于热点路径的性能有非常大的好处,正是得益于此,Rust 的异步编程性能才会这么高。 - **Rust 没有内置异步调用所必须的运行时**,但是无需担心,Rust 社区生态中已经提供了非常优异的运行时实现,例如大明星 [`tokio`](https://tokio.rs) -- **运行时同时支持单线程和多线程**,这两者拥有各自的优缺点, 稍后会讲 +- **运行时同时支持单线程和多线程**,这两者拥有各自的优缺点,稍后会讲 #### Rust: async vs 多线程 @@ -234,7 +234,7 @@ warning: unused implementer of `futures::Future` that must be used hello, world! ``` -不出所料,`main`函数中的`future`我们通过`block_on`函数进行了运行,但是这里的`hello_cat`返回的`Future`却没有任何人去执行它,不过好在编译器友善的给出了提示:`futures do nothing unless you .await or poll them `,两种解决方法:使用`.await`语法或者对`Future`进行轮询(`poll`)。 +不出所料,`main`函数中的`future`我们通过`block_on`函数进行了运行,但是这里的`hello_cat`返回的`Future`却没有任何人去执行它,不过好在编译器友善的给出了提示:```futures do nothing unless you `.await` or poll them```,两种解决方法:使用`.await`语法或者对`Future`进行轮询(`poll`)。 后者较为复杂,暂且不表,先来使用`.await`试试: diff --git a/src/async-rust/intro.md b/src/async-rust/intro.md index 416c5846..0e5123fd 100644 --- a/src/async-rust/intro.md +++ b/src/async-rust/intro.md @@ -1,5 +1,5 @@ # Rust 异步编程 -在艰难的学完 Rust 入门和进阶所有的 70 个章节后,我们终于来到了这里。假如之前攀登的是珠穆拉玛峰,那么现在攀登的就是乔戈里峰( 比珠峰还难攀爬... )。 +在艰难的学完 Rust 入门和进阶所有的 70 个章节后,我们终于来到了这里。假如之前攀登的是珠穆朗玛峰,那么现在攀登的就是乔戈里峰( 比珠峰还难攀爬... )。 如果你想开发 Web 服务器、数据库驱动、消息服务等需要高并发的服务,那么本章的内容将值得认真对待和学习,将从以下方面深入讲解 Rust 的异步编程: diff --git a/src/cargo/reference/build-script/examples.md b/src/cargo/reference/build-script/examples.md index cdba5d5f..9dd7c1e2 100644 --- a/src/cargo/reference/build-script/examples.md +++ b/src/cargo/reference/build-script/examples.md @@ -358,4 +358,4 @@ pub fn sha3_224() -> MessageDigest { } ``` -当然,大家在使用时一定要小心,因为这可能会导致生成的二进制文件进一步依赖当前的构建环境。例如,当二进制可执行文件需要在另一个操作系统中分发运行时,那它依赖的信息对于该操作系统可能是不存在! +当然,大家在使用时一定要小心,因为这可能会导致生成的二进制文件进一步依赖当前的构建环境。例如,当二进制可执行文件需要在另一个操作系统中分发运行时,那它依赖的信息对于该操作系统可能是不存在的! diff --git a/src/cargo/reference/specify-deps.md b/src/cargo/reference/specify-deps.md index 4ab2c54a..93f4d140 100644 --- a/src/cargo/reference/specify-deps.md +++ b/src/cargo/reference/specify-deps.md @@ -21,7 +21,7 @@ time = "0.1.12" 但是 `^` 能做的更多。 -> npm 使用的就是 `semver` 版本号,从 JS 过来的同学应该非常熟悉。 +> npm 使用的就是 `semver` 版本号,从 `JavaScript` 过来的同学应该非常熟悉。 #### `^` 指定版本