并发控制
同步
同步指的是线程之间的协作配合,以共同完成某个任务。在整个过程中,需要注意两个关键点:一是共享资源的访问,二是访问资源的顺序。通过前面的介绍,我们已经知道了如何让多个线程访问共享资源,但并没介绍如何控制访问顺序,才不会出现错误。如果两个线程同时访问同一内存地址的数据,一个写,一个读,如果不加控制,写线程只写了一半,读线程就开始读,必然读到的数据是错误的,不可用的,从而造成程序错误,这就造成了并发安全问题,为此我们必须要有一套控制机制来避免这样的事情发生。
Rust 中线程等待和其他语言在机制上并无差异,大致有下面几种:
-
等待一段时间后,再接着继续执行。看起来就像一个人工作累了,休息一会再工作。通过调用相关的 API 可以让当前线程暂停执行进入睡眠状态,此时调度器不会调度它执行,等过一段时间后,线程自动进入就绪状态,可以被调度执行,继续从之前睡眠时的地方执行。对应的 API 有
std::thread::sleep
,std::thread::sleep_ms
,std::thread::park_timeout
,std::thread::park_timeout_ms
。 -
这一种方式有点特殊,时间非常短,就一个时间片,当前线程自己主动放弃当前时间片的调度,让调度器重新选择线程来执行,这样就把运行机会给了别的线程,但是要注意的是,如果别的线程没有更好的理由执行,当然最后执行机会还是它的。在实际的应用业务中,比如生产者制造出一个产品后,可以放弃一个时间片,让消费者获得执行机会,从而快速地消费才生产的产品。这样的控制粒度非常小,需要合理使用,如果需要连续放弃多个时间片,可以借用循环实现。对应的 API 是
std::thread::yield_now
。 -
1 和 2 的等待都无须其他线程的协助,即可在一段时间后继续执行。最后我们还遇到一种等待,是需要其他线程参与,才能把等待的线程叫醒,否则,线程会一直等待下去。好比一个女人,要是没有遇到一个男人,就永远不可能摆脱单身的状态。相关的 API 包括
std::thread::JoinHandle::join
,std::thread::park
,std::sync::Mutex::lock
等,还有一些同步相关的类的 API 也会导致线程等待。