|
|
@ -248,7 +248,7 @@ fn main() {
|
|
|
|
|
|
|
|
|
|
|
|
在上面的描述中,我们用了"可能"二字,原因在于死锁在这段代码中不是必然发生的,总有一次运行你能看到最后一行打印输出。这是由于子线程的初始化顺序和执行速度并不确定,我们无法确定哪个线程中的锁先被执行,因此也无法确定两个线程对锁的具体使用顺序。
|
|
|
|
在上面的描述中,我们用了"可能"二字,原因在于死锁在这段代码中不是必然发生的,总有一次运行你能看到最后一行打印输出。这是由于子线程的初始化顺序和执行速度并不确定,我们无法确定哪个线程中的锁先被执行,因此也无法确定两个线程对锁的具体使用顺序。
|
|
|
|
|
|
|
|
|
|
|
|
但是,可以简单的说明下死锁发生的必然条件:线程1锁住了`mutex1`并且线程`2`锁住了`mutex2`,然后线程1试图去访问`mutex2`,同时线程`2`试图去访问`mutex1`,就会锁住。 因为线程2需要等待线程1释放`mutex1`后,才会释放`mutex2`,而与此同时,线程1需要等待线程2释放`mutex2`后才能释放`mutex1`,这种情况造成了两个线程都无法释放对方需要的锁,最终锁死。
|
|
|
|
但是,可以简单的说明下死锁发生的必然条件:线程1锁住了`mutex1`并且线程`2`锁住了`mutex2`,然后线程1试图去访问`mutex2`,同时线程`2`试图去访问`mutex1`,就会死锁。 因为线程2需要等待线程1释放`mutex1`后,才会释放`mutex2`,而与此同时,线程1需要等待线程2释放`mutex2`后才能释放`mutex1`,这种情况造成了两个线程都无法释放对方需要的锁,最终死锁。
|
|
|
|
|
|
|
|
|
|
|
|
那么为何某些时候,死锁不会发生?原因很简单,线程2在线程1锁`mutex1`之前,就已经全部执行完了,随之线程2的`mutex2`和`mutex1`被全部释放,线程1对锁的获取将不再有竞争者。 同理,线程1若全部被执行完,那线程2也不会被锁,因此我们在线程1中间加一个睡眠,增加死锁发生的概率。如果你在线程2中同样的位置也增加一个睡眠,那死锁将必然发生!
|
|
|
|
那么为何某些时候,死锁不会发生?原因很简单,线程2在线程1锁`mutex1`之前,就已经全部执行完了,随之线程2的`mutex2`和`mutex1`被全部释放,线程1对锁的获取将不再有竞争者。 同理,线程1若全部被执行完,那线程2也不会被锁,因此我们在线程1中间加一个睡眠,增加死锁发生的概率。如果你在线程2中同样的位置也增加一个睡眠,那死锁将必然发生!
|
|
|
|
|
|
|
|
|
|
|
@ -307,7 +307,7 @@ fn main() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
为了演示`try_lock`的作用,我们特定使用了之前必定会死锁的代码,并且将`lock`替换程`try_lock`,与之前的结果不同,这段代码将不会再有死锁发生:
|
|
|
|
为了演示`try_lock`的作用,我们特定使用了之前必定会死锁的代码,并且将`lock`替换成`try_lock`,与之前的结果不同,这段代码将不会再有死锁发生:
|
|
|
|
```console
|
|
|
|
```console
|
|
|
|
线程 0 锁住了mutex1,接着准备去锁mutex2 !
|
|
|
|
线程 0 锁住了mutex1,接着准备去锁mutex2 !
|
|
|
|
线程 1 锁住了mutex2, 准备去锁mutex1
|
|
|
|
线程 1 锁住了mutex2, 准备去锁mutex1
|
|
|
@ -396,7 +396,7 @@ Err("WouldBlock")
|
|
|
|
如果不是追求特别极致的性能,建议选择前者。
|
|
|
|
如果不是追求特别极致的性能,建议选择前者。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 用条件(Condvar)控制线程的同步
|
|
|
|
## 用条件变量(Condvar)控制线程的同步
|
|
|
|
`Mutex`用于解决资源安全访问的问题,但是我们还需要一个手段来解决资源访问顺序的问题。而Rust考虑到了这一点,为我们提供了条件变量(Condition Variables),它经常和`Mutex`一起使用,可以让线程挂起,直到某个条件发生后再继续执行,其实`Condvar`我们在之前的多线程章节就已经见到过,现在再来看一个不同的例子:
|
|
|
|
`Mutex`用于解决资源安全访问的问题,但是我们还需要一个手段来解决资源访问顺序的问题。而Rust考虑到了这一点,为我们提供了条件变量(Condition Variables),它经常和`Mutex`一起使用,可以让线程挂起,直到某个条件发生后再继续执行,其实`Condvar`我们在之前的多线程章节就已经见到过,现在再来看一个不同的例子:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -474,7 +474,7 @@ async fn main() {
|
|
|
|
let mut join_handles = Vec::new();
|
|
|
|
let mut join_handles = Vec::new();
|
|
|
|
|
|
|
|
|
|
|
|
for _ in 0..5 {
|
|
|
|
for _ in 0..5 {
|
|
|
|
let permit = semaphore.clone().acquire_owned().await.unwrap();
|
|
|
|
let permit = semaphore.clone().acquire_owned().await.unwrap();
|
|
|
|
join_handles.push(tokio::spawn(async move {
|
|
|
|
join_handles.push(tokio::spawn(async move {
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// 在这里执行任务...
|
|
|
|
// 在这里执行任务...
|
|
|
|