diff --git a/hello/src/main.rs b/hello/src/main.rs index 4a26a6b..549837f 100644 --- a/hello/src/main.rs +++ b/hello/src/main.rs @@ -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"), diff --git a/src/Ch20_Final_Project_Building_a_Multithreaded_Web_Server.md b/src/Ch20_Final_Project_Building_a_Multithreaded_Web_Server.md index 1e5002a..549a33a 100644 --- a/src/Ch20_Final_Project_Building_a_Multithreaded_Web_Server.md +++ b/src/Ch20_Final_Project_Building_a_Multithreaded_Web_Server.md @@ -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` 确保了一次只有一个 `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` 的生命周期,因此这个 `Mutex` 结构体没有公开的 `unlock` 方法。在编译时,借用检查器可于随后,就除非咱们拿着 `Mutex` 所守卫的某项资源的锁,否则无法访问该项资源这一规矩强制加以检查。但是,若咱们没有注意到 `MutexGuard` 的生命周期,那么这样的实现同样能导致锁相较预期被占用更长时间。 + +由于在 `let` 之下,等号右侧的表达式中用到的任何临时值,都会在 `let` 语句结束时被立即丢弃,因此使用了 `let job = receiver.lock().unwrap().recv().unwrap();` 的清单 20-20 中代码是工作的。但是,`while let`(以及 `if let` 与 `match`) 则是在相关代码块结束前,不会丢弃那些临时值。在清单 20-21 中,