rust - 如何遍历线程句柄并在完成后加入另一个循环?

标签 rust

我有一个在循环中创建线程的程序,还检查它们是否已完成并在完成时清理它们。请参阅下面的最小示例:

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.iter_mut() {
            if jh.is_finished() {
                jh.join().unwrap();
            }
        } 
    }
}

这给出了错误:

error[E0507]: cannot move out of `*jh` which is behind a mutable reference
    --> src\main.rs:13:17
     |
13   |                 jh.join().unwrap();
     |                 ^^^------
     |                 |  |
     |                 |  `*jh` moved due to this method call
     |                 move occurs because `*jh` has type `JoinHandle<()>`, which does not implement the `Copy` trait
     |
note: this function takes ownership of the receiver `self`, which moves `*jh`
    --> D:\rust\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib/rustlib/src/rust\library\std\src\thread\mod.rs:1461:17
     |
1461 |     pub fn join(self) -> Result<T> {

我怎样才能让借阅检查员允许这样做?

最佳答案

JoinHandle::join 实际上使用 JoinHandle。 但是,iter_mut() 仅借用向量的元素并使向量保持事件状态。因此,您的 JoinHandle 只是借用的,您不能在借用的对象上调用消费方法。

您需要做的是在遍历向量时获取元素的所有权,这样它们就可以被 join() 使用。这是通过使用 into_iter() 而不是 iter_mut() 实现的。

第二个错误是您(可能是不小心)将两个 for 循环写在彼此内部,而它们应该是独立的循环。

第三个问题有点复杂。您无法检查线程是否已完成,然后按照您的方式加入它。因此,我暂时删除了 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();
    }
}

对完成的线程作出 react

这个比较难。如果您只是想等到所有 都完成,上面的代码是可行的方法。

但是,如果您必须立即对完成的线程使用react,您基本上必须设置某种事件传播。您不想一遍又一遍地遍历所有线程,直到它们全部完成,因为这称为空闲等待并且会消耗大量计算能力。

所以如果你想实现,有两个问题必须要处理:

  • join() 消耗 JoinHandle(),这将留下 JoinHandle 的不完整 Vec .这是不可能的,所以我们需要将 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.");
}

重要

这段代码只是一个演示。如果其中一个线程崩溃,它将死锁。但这表明该问题有多么复杂。

它可以通过使用防坠落装置来解决,但我认为这个答案已经够复杂了;)

关于rust - 如何遍历线程句柄并在完成后加入另一个循环?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72493266/

相关文章:

loops - 迭代向量的相邻元素时如何避免循环

rust - 编写 Rust-y 代码 : Keeping references to different structs depending on type of object in XML

syntax - { .. } 在模式中意味着什么?

Rust — 为什么这里有警告 "value captured by ` online` is never read"?

rust - 如何同时获取对两个数组元素的可变引用?

reference - 引用的身份关闭

rust - 当索引检查越界时分配返回值

rust - 如何将红 bean 杉组件分离到不同的文件中?

rust - 是否可以仅为 Release模式调用 build.rs?

rust - 是否有一种安全、符合人体工程学的方法来更改复杂结构中的幻像类型?