88### 等待
99Rust中线程等待和其他语言在机制上并无差异,大致有下面几种:
1010
11- 1. 等待一段时间后,再接着继续执行。看起来就像一个人工作累了,休息一会再工作。通过调用相关的API可以让当前线程暂停执行进入睡眠状态,此时调度器不会调度它执行,等过一段时间后,线程自动进入就绪状态,可以被调度执行,继续从之前睡眠时的地方执行。对应的API有`std::thread::sleep`,`std::thread::sleep_ms`,`std::thread::park_timeout`,`std::thread::park_timeout_ms`,还有一些类似的其他API,由于太多,详细信息就请参见官网[`std::thread`](https://doc.rust-lang.org/stable/std/thread/index.html)。
12- 2. 这一种方式有点特殊,时间非常短,就一个时间片,当前线程自己主动放弃当前时间片的调度,让调度器重新选择线程来执行,这样就把运行机会给了别的线程,但是要注意的是,如果别的线程没有更好的理由执行,当然最后执行机会还是它的。在实际的应用业务中,比如生产者制造出一个产品后,可以放弃一个时间片,让消费者获得执行机会,从而快速地消费才生产的产品。这样的控制粒度非常小,需要合理使用,如果需要连续放弃多个时间片,可以借用循环实现。对应的API是`std::thread::yield_now`,详细信息参见官网[`std::thread`](https://doc.rust-lang.org/stable/std/thread/index.html)。
13- 3. 1和2的等待都无须其他线程的协助,即可在一段时间后继续执行。最后我们还遇到一种等待,是需要其他线程参与,才能把等待的线程叫醒,否则,线程会一直等待下去。好比一个女人,要是没有遇到一个男人,就永远不可能摆脱单身的状态。相关的API包括`std::thread::JoinHandle::join`,`std::thread::park`,`std::sync::Mutex::lock`等,还有一些同步相关的类的API也会导致线程等待。详细信息参见官网[`std::thread`](https://doc.rust-lang.org/stable/std/thread/index.html)和[`std::sync`](https://doc.rust-lang.org/stable/std/sync/index.html)。
11+ * 等待一段时间后,再接着继续执行。看起来就像一个人工作累了,休息一会再工作。通过调用相关的API可以让当前线程暂停执行进入睡眠状态,此时调度器不会调度它执行,等过一段时间后,线程自动进入就绪状态,可以被调度执行,继续从之前睡眠时的地方执行。对应的API有` std::thread::sleep ` ,` std::thread::sleep_ms ` ,` std::thread::park_timeout ` ,` std::thread::park_timeout_ms ` ,还有一些类似的其他API,由于太多,详细信息就请参见官网[ ` std::thread ` ] ( https://doc.rust-lang.org/stable/std/thread/index.html ) 。
12+ * 这一种方式有点特殊,时间非常短,就一个时间片,当前线程自己主动放弃当前时间片的调度,让调度器重新选择线程来执行,这样就把运行机会给了别的线程,但是要注意的是,如果别的线程没有更好的理由执行,当然最后执行机会还是它的。在实际的应用业务中,比如生产者制造出一个产品后,可以放弃一个时间片,让消费者获得执行机会,从而快速地消费才生产的产品。这样的控制粒度非常小,需要合理使用,如果需要连续放弃多个时间片,可以借用循环实现。对应的API是` std::thread::yield_now ` ,详细信息参见官网[ ` std::thread ` ] ( https://doc.rust-lang.org/stable/std/thread/index.html ) 。
13+ * 1和2的等待都无须其他线程的协助,即可在一段时间后继续执行。最后我们还遇到一种等待,是需要其他线程参与,才能把等待的线程叫醒,否则,线程会一直等待下去。好比一个女人,要是没有遇到一个男人,就永远不可能摆脱单身的状态。相关的API包括` std::thread::JoinHandle::join ` ,` std::thread::park ` ,` std::sync::Mutex::lock ` 等,还有一些同步相关的类的API也会导致线程等待。详细信息参见官网[ ` std::thread ` ] ( https://doc.rust-lang.org/stable/std/thread/index.html ) 和[ ` std::sync ` ] ( https://doc.rust-lang.org/stable/std/sync/index.html ) 。
1414
1515第一种和第三种等待方式,其实我们在上面的介绍中,都已经遇到过了,它们也是使用的最多的两种方式。在此,也可以回过头去看看前面的使用方式和使用效果,结合自己的理解,做一些简单的练习。
1616
@@ -19,11 +19,11 @@ Rust中线程等待和其他语言在机制上并无差异,大致有下面几
1919### 通知
2020看是简单的通知,在编程时也需要注意以下几点:
2121
22- 1. 通知必然是因为有等待,所以通知和等待几乎都是成对出现的,比如`std::sync::Condvar::wait`和`std::sync::Condvar::notify_one`,`std::sync::Condvar::notify_all`。
23- 2. 等待所使用的对象,与通知使用的对象是同一个对象,从而该对象需要在多个线程之间共享,参见下面的例子。
24- 3. 除了`Condvar`之外,其实*锁*也是具有自动通知功能的,当持有锁的线程释放锁的时候,等待锁的线程就会自动被唤醒,以抢占锁。关于锁的介绍,在下面有详解。
25- 4. 通过条件变量和锁,还可以构建更加复杂的自动通知方式,比如`std::sync::Barrier`。
26- 5. 通知也可以是1:1的,也可以是1:N的,`Condvar`可以控制通知一个还是N个,而锁则不能控制,只要释放锁,所有等待锁的其他线程都会同时醒来,而不是只有最先等待的线程。
22+ * 通知必然是因为有等待,所以通知和等待几乎都是成对出现的,比如` std::sync::Condvar::wait ` 和` std::sync::Condvar::notify_one ` ,` std::sync::Condvar::notify_all ` 。
23+ * 等待所使用的对象,与通知使用的对象是同一个对象,从而该对象需要在多个线程之间共享,参见下面的例子。
24+ * 除了` Condvar ` 之外,其实* 锁* 也是具有自动通知功能的,当持有锁的线程释放锁的时候,等待锁的线程就会自动被唤醒,以抢占锁。关于锁的介绍,在下面有详解。
25+ * 通过条件变量和锁,还可以构建更加复杂的自动通知方式,比如` std::sync::Barrier ` 。
26+ * 通知也可以是1:1的,也可以是1: N 的,` Condvar ` 可以控制通知一个还是N个,而锁则不能控制,只要释放锁,所有等待锁的其他线程都会同时醒来,而不是只有最先等待的线程。
2727
2828下面我们分析一个简单的例子:
2929
@@ -63,10 +63,10 @@ after wait
6363```
6464这个例子展示了如何通过条件变量和锁来控制新建线程和主线程的同步,让主线程等待新建线程执行后,才能继续执行。从结果来看,功能上是实现了。对于上面这个例子,还有下面几点需要说明:
6565
66- 1. `Mutex`是Rust中的一种锁。
67- 2. `Condvar`需要和`Mutex`一同使用,因为有`Mutex`保护,`Condvar`并发才是安全的。
68- 3. `Mutex::lock`方法返回的是一个`MutexGuard`,在离开作用域的时候,自动销毁,从而自动释放锁,从而避免锁没有释放的问题。
69- 4. `Condvar`在等待时,时会释放锁的,被通知唤醒时,会重新获得锁,从而保证并发安全。
66+ * ` Mutex ` 是Rust中的一种锁。
67+ * ` Condvar ` 需要和` Mutex ` 一同使用,因为有` Mutex ` 保护,` Condvar ` 并发才是安全的。
68+ * ` Mutex::lock ` 方法返回的是一个` MutexGuard ` ,在离开作用域的时候,自动销毁,从而自动释放锁,从而避免锁没有释放的问题。
69+ * ` Condvar ` 在等待时,时会释放锁的,被通知唤醒时,会重新获得锁,从而保证并发安全。
7070
7171到此,你应该对锁比较感兴趣了,为什么需要锁?锁存在的目的就是为了保证资源在同一个时间,能有序地被访问,而不会出现异常数据。但其实要做到这一点,也并不是只有锁,包括锁在内,主要涉及两种基本方式:
7272
@@ -166,4 +166,4 @@ fn main() {
166166
167167在Rust中,` Mutex ` 是一种独占锁,同一时间只有一个线程能持有这个锁。这种锁会导致所有线程串行起来,这样虽然保证了安全,但效率并不高。对于写少读多的情况来说,如果在没有写的情况下,都是读取,那么应该是可以并发执行的,为了达到这个目的,几乎所有的编程语言都提供了一种叫读写锁的机制,Rust中也存在,叫[ ` std::sync::RwLock ` ] ( https://doc.rust-lang.org/std/sync/struct.RwLock.html ) ,在使用上同` Mutex ` 差不多,在此就留给大家自行练习了。
168168
169- 同步是多线程编程的永恒主题,Rust已经为我们提供了良好的编程范式,并强加检查,即使你之前没有怎么接触过,用Rust也能编写出非常安全的多线程程序。
169+ 同步是多线程编程的永恒主题,Rust已经为我们提供了良好的编程范式,并强加检查,即使你之前没有怎么接触过,用Rust也能编写出非常安全的多线程程序。
0 commit comments