17.1 线程
17.1.1 spawn:创建新线程
thread::spawn
:创建一个新线程,需要传递一个闭包,并在其中包含希望在新线程运行的代码thread::sleep
:调用强制线程停止执行一小段时间。比如:thread::sleep(Duration::from_millis(1));
17.1.2 join:等待所有线程结束
thread::spawn
的返回值类型是JoinHandle
,JoinHandle
是一个拥有所有权的值JoinHandle.join
:通过调用handle
的join
会阻塞当前线程直到handle
所代表的线程结束(也就是等待所关联的线程运行结束)join
的调用位置会影响线程的运行顺序(比如:影响线程是否同时运行)
17.1.3 线程与 move 闭包
- 可以在参数列表前使用
move
关键字强制闭包获取其使用的环境值的所有权。比如:使用main
函数中的外部数据,这样就无法知道这些外部数据的生命周期,因此无法编译 move
关键字经常用于传递给thread::spawn
的闭包,因为闭包会获取从环境中取得的值的所有权,因此会将这些值的所有权从一个线程传送到另一个线程move
关键字覆盖了 Rust 默认保守的借用,但它不允许我们违反所有权规则
17.2 使用消息传递在线程间传送数据
17.2.1 消息传递(message passing)与信道(channel)
use std::sync::mpsc;
:mpsc
是 多个生产者,单个消费者(multiple producer, single consumer)的缩写。简而言之,Rust 标准库实现信道的方式意味着一个信道可以有多个产生值的 发送(sending)端,但只能有一个消费这些值的 接收(receiving)端mpsc::channel
函数返回一个元组:第一个元素是发送端(发送者,tx),而第二个元素是接收端(接收者,rx,具有迭代器特性)- 发送者方法:
send
(转移所有权) send
(转移所有权):用来获取需要放入信道的值;该方法返回一个 Result<T, E> 类型,所以如果接收端已经被丢弃了,将没有发送值的目标,所以发送操作会返回错误- 接收者方法:
recv
、try_recv
recv
:该方法会阻塞主线程执行直到从信道中接收一个值(即:一直等待消息,直到接收到一个值才会结束)。一旦发送了一个值,recv
会在一个Result<T, E>
中返回它。当信道发送端关闭,recv
会返回一个错误表明不会再有新的值到来了try_recv
:不会阻塞,相反它立刻返回一个 Result<T, E>:Ok 值包含可用的信息,而 Err 值代表此时没有任何消息。如果线程在等待消息过程中还有其他工作时使用 try_recv 很有用
17.2.2 信道与所有权转移
send
:函数获取其参数的所有权并移动这个值归接收者所有
17.2.3 发送多个值并观察接收者的等待
- 接收者
rx
是一个迭代器
17.2.4 通过克隆发送者来创建多个生产者
- 注意:下面的例子,是不确定顺序的实现!可以添加
join
来阻塞线程,进而控制接收消息的先后顺序
17.3 共享状态并发
17.3.1 互斥器一次只允许一个线程访问数据
- 互斥器(mutex)在任意时刻只允许一个线程访问某些数据。为了访问互斥器中的数据,线程首先需要通过**获取互斥器的 锁(lock)**来表明其希望访问数据
17.3.2 互斥器 Mutex<T>
use std::sync::Mutex;
Mutex<T>
:使用关联函数new
来创建一个Mutex<T>
,Mutex<T>
是一个智能指针;Mutex<T>
提供了内部可变性,就像 Cell 系列类型那样lock
:获取锁,以访问互斥器中的数据,返回一个叫做MutexGuard
的智能指针(这个智能指针实现了Deref
来指向其内部数据;其也提供了一个Drop
实现当MutexGuard
离开作用域时自动释放锁)。这个调用会阻塞当前线程,直到我们拥有锁为止- 锁的特性,如下所示
- 如果另一个线程拥有锁,并且那个线程 panic 了,则 lock 调用会失败。在这种情况下,没人能够再获取锁(所以这里可以选择
unwrap
并在遇到这种情况时使线程panic
) - 一旦获取了锁,就可以将返回值视为一个其内部数据的可变引用了(在示例中,
m
的类型是Mutex<i32>
而不是i32
,所以必须获取锁才能使用这个i32
值)
17.3.3 在线程间共享 Mutex<T>(一):不能将锁的所有权移动到多个线程中
17.3.4 在线程间共享 Mutex<T>(二):多线程和多所有权
Rc<T>
并不能安全的在线程间共享
17.3.5 在线程间共享 Mutex<T>(三):原子引用计数 Arc<T>
Arc<T>
:原子引用计数(atomically reference counted)类型,一个类似Rc<T>
并可以安全的用于并发环境的类型。Arc<T>
和Rc<T>
有着相同的 APIMutex<T>
提供了内部可变性,就像 Cell 系列类型那样,因此如同使用RefCell<T>
可以改变Rc<T>
中的内容那样,同样的可以使用Mutex<T>
来改变Arc<T>
中的内容
17.3.6 RefCell<T>/Rc<T> 与 Mutex<T>/Arc<T> 的相似性
17.4 使用 Sync 和 Send trait 的可扩展并发
17.4.1 通过 Send 允许在线程间转移所有权
Send
trait(所有权):一个实现了Send
的类型值的所有权可以在线程间传送Send
的类型:几乎所有的 Rust(基本)类型都是Send
的、完全由Send
的类型组成的类型也会自动被标记为Send
、Arc<T>
- 非
Send
的类型::Rc<T>
、裸指针(raw pointer)
17.4.2 Sync 允许多线程访问
Sync
trait(引用):一个实现了Sync
的类型可以安全的在多个线程中拥有其值的引用Sync
的类型:基本类型是Sync
的、完全由Sync
的类型组成的类型也是Sync
的、Mutex<T>
- 非
Sync
的类型:Rc<T>
、RefCell<T>
、Cell<T>
17.4.3 手动实现 Send 和 Sync 是不安全的
- 注意:通常并不需要手动实现
Send
和Sync
trait!