diff --git a/second-edition/dictionary.txt b/second-edition/dictionary.txt index 3f2c8f43a..f5b416ea9 100644 --- a/second-edition/dictionary.txt +++ b/second-edition/dictionary.txt @@ -228,6 +228,7 @@ lifecycle LimitTracker lobally locators +LockResult login lookup loopback diff --git a/second-edition/src/ch20-05-sending-requests-via-channels.md b/second-edition/src/ch20-05-sending-requests-via-channels.md index a957bfd4f..1532b0d42 100644 --- a/second-edition/src/ch20-05-sending-requests-via-channels.md +++ b/second-edition/src/ch20-05-sending-requests-via-channels.md @@ -498,6 +498,52 @@ never create more than four threads, so our system won’t get overloaded if the server gets a lot of requests. If we make a request to `/sleep`, the server will be able to serve other requests by having another thread run them. +After learning about the `while let` loop in Chapter 18, you might be +wondering why we didn't write the worker thread like this: + +Filename: src/lib.rs + +```rust,ignore +// ...snip... + +impl Worker { + fn new(id: usize, receiver: Arc>>) -> Worker { + let thread = thread::spawn(move || { + while let Ok(job) = receiver.lock().unwrap().recv() { + println!("Worker {} got a job; executing.", id); + + job.call_box(); + } + }); + + Worker { + id, + thread, + } + } +} +``` + +Listing 20-22: An alternative implementation of the worker +thread using `while let` + +This code compiles and runs, but doesn't result in the desired threading +behavior. The reason why is somewhat subtle: the `Mutex` struct has no public +`unlock` method because the ownership of the lock is based on the lifetime of +the `MutexGuard` within the `LockResult>` that `lock` returns. +This allows the borrow checker to enforce at compile time that we never access +a resource guarded by a `Mutex` without holding the lock, but it can also result +in holding the lock longer than intended if you don't think carefully about +the lifetime of the `MutexGuard`. Since the values in the the `while` +expression remain in scope for the duration of the following block, the lock +remains held for the duration of the call to `job.call_box()`, meaning other +workers cannot receive jobs. + +By using `loop` instead, the `MutexGuard` is dropped as soon as the `let job` +statement ends. This ensures that the lock is held during the call to `recv`, +but it is released before the call to `job.call_box()`, allowing multiple +requests to be serviced concurrently. + What about those warnings, though? Don’t we use the `workers`, `id`, and `thread` fields? Well, right now, we’re using all three of these fields to hold onto some data, but we don’t actually *do* anything with the data once we’ve