Update Ch20

This commit is contained in:
Unisko PENG 2023-04-06 14:07:53 +08:00
parent 3d8950eb86
commit 9b46dc2e4b
2 changed files with 35 additions and 1 deletions

View File

@ -29,7 +29,7 @@ fn handle_conn(mut stream: TcpStream) {
let (status_line, filename) = match &req_line[..] {
"GET / HTTP/1.1" => ( "HTTP/1.1 200 OK", "hello.html"),
"GET /sleep HTTP/1.1" => {
thread::sleep(Duration::from_secs(10));
thread::sleep(Duration::from_secs(30));
("HTTP/1.1 200 0K", "hello.html")
}
_ => ("HTTP/1.1 404 NOT FOUND", "404.html"),

View File

@ -1109,4 +1109,38 @@ impl Worker {
*清单 20-20`worker` 的线程中接收并执行作业*
这里,咱们首选调用了 `receiver` 上的 `lock` 来请求那个互斥量mutex并于随后调用 `unwrap` 来在出现任何错误时终止运行。在互斥量处于 *中毒poisoned* 状态时,请求锁就会失败,在有别的某个线程终止运行的同时,持有着而没有释放该锁时,这种情况便会发生。在这种情况下,调用 `unrap` 来让这个线程终止运行,便是要采取的正确措施。请放心地把这个 `unwrap`,修改为一个带有对咱们有意义报错信息的 `expect`
当咱们获得了那个互斥量上的锁时,咱们就会调用 `recv` 来从通道接收一个 `Job`。最后的 `unwrap` 也会带过这里的任何错误,在持有 `sender` 的线程已关闭时就会发生这些错误,就跟 `receiver` 关闭时那个 `send` 方法会返回 `Err` 类似。
`recv` 的调用会阻塞,因此在尚无作业时,当前线程将等待,直到有某项作业可用。`Mutex<T>` 确保了一次只有一个 `Worker` 线程是在尝试请求作业。
咱们的线程池现在就处于工作状态了!给他一次 `cargo run` 并构造一些请求:
```console
$ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.00s
Running `target\debug\hello.exe`
Worker 0 获取到一项作业;执行中。
Worker 1 获取到一项作业;执行中。
Worker 2 获取到一项作业;执行中。
Worker 3 获取到一项作业;执行中。
Worker 0 获取到一项作业;执行中。
Worker 1 获取到一项作业;执行中。
Worker 1 获取到一项作业;执行中。
Worker 2 获取到一项作业;执行中。
Worker 3 获取到一项作业;执行中。
Worker 0 获取到一项作业;执行中。
Worker 1 获取到一项作业;执行中。
```
成功了!咱们现在有了一个会异步执行 TCP 连接的线程池。绝不会有超过四个线程被创建出来,因此在服务器收到很多请求时,咱们的系统将不会过载。在咱们构造了一个到 `/sleep` 的请求时,服务器通过让另一线程运行别的一些请求,而将能服务这些请求。
> 注意:若咱们在多个窗口同时打开 `/sleep`,他们可能会在设置的时间间隔每次加载一个。有些 web 浏览器会出于缓存原因,而顺序执行同一请求的多个实例。这样的局限并不是由咱们的服务器导致的。
这段代码将会编译及运行,但不会产生所需的线程行为:慢速请求仍将导致别的请求等待被处理。至于原因则有点微妙:由于锁的所有权是基于 `lock` 方法返回的 `LockResult<MutexGuard<T>>` 中,`MutexGuard<T>` 的生命周期,因此这个 `Mutex` 结构体没有公开的 `unlock` 方法。在编译时,借用检查器可于随后,就除非咱们拿着 `Mutex` 所守卫的某项资源的锁,否则无法访问该项资源这一规矩强制加以检查。但是,若咱们没有注意到 `MutexGuard<T>` 的生命周期,那么这样的实现同样能导致锁相较预期被占用更长时间。
由于在 `let` 之下,等号右侧的表达式中用到的任何临时值,都会在 `let` 语句结束时被立即丢弃,因此使用了 `let job = receiver.lock().unwrap().recv().unwrap();` 的清单 20-20 中代码是工作的。但是,`while let`(以及 `if let``match` 则是在相关代码块结束前,不会丢弃那些临时值。在清单 20-21 中,