当我和Condvar
一起玩时,我有意想不到的结果。我知道我不能相信我的wait()
不会提前醒来,但在我的情况下,似乎我的一个醒来一直在失踪。鉴于此示例代码:
use std::sync::{Arc, Mutex, Condvar};
use std::{
thread,
time
};
fn main() {
let pair = Arc::new((Mutex::new(false), Condvar::new()));
let pause = time::Duration::from_secs(1);
for id in 0..10 {
let pair2 = pair.clone();
thread::spawn(move|| {
let &(ref lock, ref cvar) = &*pair2;
let lock = lock.lock().unwrap();
let _ = cvar.wait(lock).unwrap();
println!("Thread {} done!", id);
});
}
// Wait for the thread to start up.
let &(ref _lock, ref cvar) = &*pair;
for _ in 0..10 {
thread::sleep(pause);
cvar.notify_one();
}
}
我只是从循环中获得前八个线程:
Thread 0 done!
Thread 1 done!
Thread 2 done!
Thread 3 done!
Thread 4 done!
Thread 5 done!
Thread 6 done!
Thread 7 done!
Thread 8 done!
如果我将第二个循环的计数增加到11,它实际上会唤醒所有九个循环。
我在the documentation和wait()
上双重检查了notify_one()
,但这里的问题并不明显。
有什么想法吗?这是一个错误还是我做得不对的事情?
您的主线程不会等待工作线程完成,并且根据the documentation of std::thread::spawn
:
[...]子线程可能比父线程更长(除非父线程是主线程;当主线程完成时整个进程终止)。
(重点是我的)
因为您的程序在最后一个notify_one
之后立即终止,所以工作线程可能会在打印值之前被终止。
当你循环11次时,在第10个notify_all
之后有一个1s的睡眠,这意味着最新的工作线程可能会完成它的工作。
这个问题的一个常见解决方案是收集JoinHandle
返回的spawn
s,并根据sn99's answer等待它们。
将代码更改为:
use std::sync::{Arc, Condvar, Mutex};
use std::{thread, time};
fn main() {
let pair = Arc::new((Mutex::new(false), Condvar::new()));
let pause = time::Duration::from_secs(1);
// Create a vector of `JoinHandle`
let mut thread_handles = Vec::new();
for id in 0..10 {
let pair2 = pair.clone();
// Push JoinHandle in the vector
thread_handles.push(thread::spawn(move || {
let &(ref lock, ref cvar) = &*pair2;
let lock = lock.lock().unwrap();
let _ = cvar.wait(lock).unwrap();
println!("Thread {} done!", id);
}));
}
// Wait for the thread to start up.
let &(ref _lock, ref cvar) = &*pair;
for _ in 0..10 {
thread::sleep(pause);
cvar.notify_one();
}
// Wait for all threads to complete before exiting `main`
// handles is of type JoinHandle
for handles in thread_handles {
handles.join().unwrap();
}
}
join
方法等待所有线程完成。这是必要的,因为一旦主要返回,Rust程序就会退出,即使其他线程仍在运行。