Spawning tasks 上一个练习的解答应该类似于这样: pub async fn echo(listener: TcpListener) -> Result<(), anyhow::Error> { loop { let (mut socke
异步函数 我们目前编写的所有函数和方法都是“急切型”的。 在我们调用它们之前,它们什么都不会发生。但一旦调用,它们就会一直运行到完成:完成所有工作,然后返回结果。 有时,这种做法并不理想。 例如,如果我们正在编写一个 HTTP 服务器,可能会有很多等待:等待请求体到达、等待数据库响应、等待下游服务响
线程并非 Rust 中编写并发程序的唯一方法。 本章我们将探索另一种方法:异步编程(asynchronous programming)。 具体来说,我们可以学到: async/.await 关键字,我们可以轻松编写异步代码 Future trait,用于表示尚未完成的运算 tokio,最流行的异步
Sync(同步) 在结束本章之前,我们来学习一下 Rust 标准库中的另一个关键特性:同步 (Sync)。 同步 (Sync) 是一个自动特性,就像 Send 一样。 所有可以在线程间安全共享的类型都会自动实现同步特性。 换句话说:如果 &T 是 Send,则 T 是Sync。 T:Sync
## Readers and writers 我们新的`TicketStore`工作正常,但读取性能不佳:由于 `Mutex` 无法区分readers和writers,因此同一时间只能有一个客户端读取特定的工单。 我们可以通过使用不同的锁定原语 `RwLock` 来解决这个问题。`RwLock`
Locks, Send and Arc 上一节实现的补丁策略有个问题:就是它很racy。(不太清楚为什么用这个词) 如果两个客户端在同一时间为同一个ticket发送patches,服务器将会以任意顺序执行他们。而最后一个被处理的patch会覆盖掉上一个进行更改的patch。 版本号 我们可以尝试使用
更新操作 到目前为止,我们值实现了插入和检索操作。让我看一下如何拓展系统,使其支持更新操作。 传统的更新 在单线程系统中,更新操作相当直观:TIcketStore提供一个get_mut方法,允许调用者获取一个可变的ticket引用,然后直接修改。 多线程更新 同样的操作方式在多线程版本中就不行。借用
有界通道和无界通道 到目前为止,我们一直在使用无界通道。我们想发送多少消息就发送多少消息,通道也会相应增长以容纳这些消息。 在多生产者,单消费者的情况下这可能会产生问题:如果生产者以比消费者处理信息更快的速度发出信息,通道就会不断增长,可能会消耗掉所有可用内存。 建议是,生产系统中绝不要使用无界通道
专有Client类型 客户端的所有交互都相当低级:我们必须手动建立一个响应通道,建立命令,将其发送到服务器,然后在响应通道上调用 recv 来获取响应。 这是大量可以抽象掉的模板代码,而这正是我们在本练习中要做的。 练习 use crate::data::{Ticket,
双向通信 在我们当前的客户端-服务器实现中,通信是单向的:从客户端到服务器。 客户端无法知道服务器是否收到了信息、执行成功还是失败。这并不理想。 为了解决这个问题,我们可以引入双向通信系统。 响应通道 我们需要一种方法让服务器向客户端发回响应。有多种方法可以做到这一点,但最简单的方法是