Update Ch20

This commit is contained in:
Peng Hailin, 2023-04-06 07:49:25 +08:00
parent 737126e2ec
commit a3baf7550b
3 changed files with 95 additions and 5 deletions

View File

@ -1,12 +1,15 @@
#![allow(warnings)]
use std::{sync::mpsc, thread};
use std::{
sync::{mpsc, Arc, Mutex},
thread,
};
pub struct ThreadPool {
workers: Vec<Worker>,
sender: mpsc::Sender<Job>,
}
struct Job;
type Job = Box<dyn FnOnce() + Send + 'static>;
impl ThreadPool {
/// 创建出一个新的 ThreadPool。
@ -21,10 +24,12 @@ impl ThreadPool {
let (sender, receiver) = mpsc::channel();
let receiver = Arc::new(Mutex::new(receiver));
let mut workers = Vec::with_capacity(size);
for id in 0..size {
workers.push(Worker::new(id, receiver));
workers.push(Worker::new(id, Arc::clone(&receiver)));
}
ThreadPool { workers, sender }
@ -34,6 +39,9 @@ impl ThreadPool {
where
F: FnOnce() + Send + 'static,
{
let job = Box::new(f);
self.sender.send(job).unwrap();
}
}
@ -43,7 +51,7 @@ struct Worker {
}
impl Worker {
fn new(id: usize, receiver: mpsc::Receiver<Job>) -> Worker {
fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Job>>>) -> Worker {
let thread = thread::spawn(|| {
receiver;
});

View File

@ -951,7 +951,7 @@ Rust 的类型系统有着一些到目前为止咱们曾提到过但尚未讨论
新类型还可以隐藏内部实现。比如,咱们可提供一个 `People` 类型,来封装一个存储着某人与其名字关联的 ID 的 `HashMap<i32, String>`。使用 `People` 的代码,只需与咱们提供的公开 API比如某个将名字字符串添加到 `People` 集合的方法交互;那些代码将不需要知悉咱们在内部分配了`i32` 的 ID 给那些名字。新型模式是达成,咱们曾在第 17 章讨论过的 [“隐藏实现细节的封装”](Ch17_Object_Oriented_Programming_Features_of_Rust.md#encapsulation-that-hides-implementation-details") 的一种轻量方式。
### 使用类型别名创建类型同义词
### <a id="creating-type-synonyms-with-type-aliases"></a>使用类型别名创建类型同义词
**Creating Type Synonyms with Type Aliases**

View File

@ -1001,4 +1001,86 @@ For more information about this error, try `rustc --explain E0382`.
error: could not compile `hello` due to previous error
```
这段代码试图将 `receiver` 传递给多个 `Worker` 实例。正如回顾到第 16 章这样做不会工作Rust 所提供的通道实现,属于多 `producer`、单 `consumer` 的。这意味着咱们不能只克隆通道的消费端来修复这段代码。咱们也不打算将一条消息,多次发送给多个消费者;咱们是要一个带有多个 `worker` 的消息列表,如此每条消息,都将被一次性处理。
此外,从通道队列里取出一项作业,还涉及到令 `receiver` 可变,因此这些县城就需要一种共用与修改 `receiver` 的安全方式;否则,咱们就会面临竞争情形(如同第 16 章中所讲到的)。
回顾第 16 章中曾讨论过的线程安全灵巧指针:为在多个线程间共用所有权,以及实现这些线程修改值,咱们需要用到 `Arc<Mutex<T>>`。`Arc` 类型将实现多个 `worker` 都拥有那个 `receiver`,而 `Mutex` 将确保某个时刻只有一个 `worker``receiver` 获取一项作业。下面清单 20-18 给出了咱们需要作出的修改。
文件名:`src/lib.rs`
```rust
use std::{
sync::{mpsc, Arc, Mutex},
thread,
};
// --跳过代码--
impl ThreadPool {
// --跳过代码--
pub fn new(size: usize) -> ThreadPool {
assert! (size > 0);
let (sender, receiver) = mpsc::channel();
let receiver = Arc::new(Mutex::new(receiver));
let mut workers = Vec::with_capacity(size);
for id in 0..size {
workers.push(Worker::new(id, Arc::clone(&receiver)));
}
ThreadPool { workers, sender }
}
// --跳过代码--
}
// --跳过代码--
impl Worker {
fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Job>>>) -> Worker {
// --跳过代码--
}
}
```
*清单 20-18运用 `Arc``Mutex` 在那些 `worker` 间共用 `receiver`*
`ThreadPool::new` 中,咱们把 `receiver` 放入到一个 `Arc` 与一个 `Mutex` 中。对于各个新 `worker`,咱们克隆了那个 `Arc`,从而增加了引用计数,这样那些 `worker` 就可以共用 `receiver` 的所有权。
有了这些修改,代码就会编译了!咱们就要达到目的了!
#### 实现 `execute` 方法
**Implementing the `execute` method**
咱们来最终实现那个 `ThreadPool` 上的 `execute` 方法。咱们还将把 `Job` 从结构体,修改为保存着 `execute` 接收到闭包类型的特质对象的类型别名。正如第 19 章 [“使用类型别名创建类型同义词”](Ch19_Advanced_Features.md#creating-type-synonyms-with-type-aliases) 小节中曾讨论过的,类型别名实现了为易于使用而将长类型构造缩短。请看看下面清单 20-19.
文件名:`src/lib.rs`
```rust
// --跳过代码--
type Job = Box<dyn FnOnce() + Send + 'static>;
impl ThreadPool {
// --跳过代码--
pub fn execute<F>(&self, f: F)
where
F: FnOnce() + Send + 'static,
{
let job = Box::new(f);
self.sender.send(job).unwrap();
}
}
// --跳过代码--
```
*清单 20-19为保存着各个闭包的 `Box` 创建出 `Job` 类型别名,并于随后把该项作业下发到通道*