JoinHandle::join
实际上consumes连接句柄。iter_mut()
然而,仅借用向量的元素并使向量保持活动状态。因此你的JoinHandle
s 只是借用的,并且您不能对借用的对象调用消费方法。
您需要做的是在迭代向量时获取元素的所有权,以便随后可以使用它们join()
。这是通过使用实现的into_iter()
代替iter_mut()
.
第二个错误是你(可能不小心)写了这两个for
彼此内部循环,但它们应该是独立的循环。
第三个问题稍微复杂一些。你不能check如果线程已完成,然后按照您的方式加入它。因此我删除了is_finished()
现在检查一下,然后再进一步讨论这个问题。
这是您的固定代码:
use std::thread;
fn main() {
let mut v = Vec::<std::thread::JoinHandle<()>>::new();
for _ in 0..10 {
let jh = thread::spawn(|| {
thread::sleep(std::time::Duration::from_secs(1));
});
v.push(jh);
}
for jh in v.into_iter() {
jh.join().unwrap();
}
}
对已完成的线程做出反应
这个比较难。如果你只是想等到all其中已经完成,上面的代码就是要走的路。
但是,如果您have要立即对已完成的线程做出反应,您基本上必须设置某种事件传播。您不想一遍又一遍地循环所有线程,直到它们全部完成,因为这就是所谓的空闲等待并且消耗大量的计算能力。
因此,如果要实现这一目标,必须解决两个问题:
-
join()
消耗JoinHandle()
,这会留下不完整的Vec
of JoinHandle
s。这是不可能的,所以我们需要包装JoinHandle
实际上可以部分地从向量中剥离出来的类型,例如Option
.
- 我们需要一种方法向主线程发出信号,表明新的子线程已完成,以便主线程不必不断迭代线程。
总而言之,实施起来非常复杂且棘手。
这是我的尝试:
use std::{
thread::{self, JoinHandle},
time::Duration,
};
fn main() {
let mut v: Vec<Option<JoinHandle<()>>> = Vec::new();
let (send_finished_thread, receive_finished_thread) = std::sync::mpsc::channel();
for i in 0..10 {
let send_finished_thread = send_finished_thread.clone();
let join_handle = thread::spawn(move || {
println!("Thread {} started.", i);
thread::sleep(Duration::from_millis(2000 - i as u64 * 100));
println!("Thread {} finished.", i);
// Signal that we are finished.
// This will wake up the main thread.
send_finished_thread.send(i).unwrap();
});
v.push(Some(join_handle));
}
loop {
// Check if all threads are finished
let num_left = v.iter().filter(|th| th.is_some()).count();
if num_left == 0 {
break;
}
// Wait until a thread is finished, then join it
let i = receive_finished_thread.recv().unwrap();
let join_handle = std::mem::take(&mut v[i]).unwrap();
println!("Joining {} ...", i);
join_handle.join().unwrap();
println!("{} joined.", i);
}
println!("All joined.");
}
重要的
这段代码只是一个演示。它会陷入僵局如果其中一个线程出现恐慌。但这表明这个问题有多么复杂。
可以通过使用下降防护装置来解决这个问题,但我认为这个答案已经足够复杂了;)