关于Rust中std::thread::JoinHandle::join方法的疑惑?

祝大家中秋节快乐

   《八月十五日夜湓(Pén)亭望月》 - 唐·白居易

    昔年八月十五夜,曲江池畔杏园边。

    今年八月十五夜,湓浦沙头水馆前。

    西北望乡何处是,东南见月几回圆。

    昨风一吹无人会,今夜清光似往年。

描述:

下面的代码源自 Rust By Example - Channels
其中的一些注释能够帮助理解代码含义

use std::sync::mpsc::{Sender, Receiver};
use std::sync::mpsc;
use std::thread;

static NTHREADS: i32 = 3;

fn main() {
    // Channels have two endpoints: the `Sender<T>` and the `Receiver<T>`,
    // where `T` is the type of the message to be transferred
    // (type annotation is superfluous)
    let (tx, rx): (Sender<i32>, Receiver<i32>) = mpsc::channel();
    let mut children = Vec::new();

    for id in 0..NTHREADS {
        // The sender endpoint can be copied
        let thread_tx = tx.clone();

        // Each thread will send its id via the channel
        let child = thread::spawn(move || {
            // The thread takes ownership over `thread_tx`
            // Each thread queues a message in the channel
            thread_tx.send(id).unwrap();

            // Sending is a non-blocking operation, the thread will continue
            // immediately after sending its message
            println!("thread {} finished", id);
        });

        children.push(child);
    }

    // Here, all the messages are collected
    let mut ids = Vec::with_capacity(NTHREADS as usize);
    for _ in 0..NTHREADS {
        // The `recv` method picks a message from the channel
        // `recv` will block the current thread if there are no messages available
        ids.push(rx.recv());
    }
    
    // Wait for the threads to complete any remaining work
    for child in children {
        child.join().expect("oops! the child thread panicked");
    }

    // Show the order in which the messages were sent
    println!("{:?}", ids);
}

下面的代码和上面相同,只是我把上面的代码的英文注释删除,并添加自己的注解说明;自己添加的这些注解可能会有错误或者不严谨,同时也在注解提出自己的疑惑

static NTHREADS: i32 = 3;

fn main() {

    let (tx, rx): (Sender<i32>, Receiver<i32>) = mpsc::channel();
    let mut children = Vec::new();

    for id in 0..NTHREADS {
        let thread_tx = tx.clone();
        //创建线程并执行,同时返回JoinHandle结构体
        //关于thread::spawn的说明下面有链接
        let child = thread::spawn(move || {
            //关于 std::sync::mpsc::channel send的说明下面有链接
            thread_tx.send(id).unwrap();
            println!("thread {} finished", id);
        });

        children.push(child);
    }

    let mut ids = Vec::with_capacity(NTHREADS as usize);
    for _ in 0..NTHREADS {
        //如果没有消息可读的时候;recv方法会阻塞当前线程(也就是main线程)的执行;
        //也就是说当前 for 循环执行完之后;我们可以确定的是 前面创建的线程都已经执行完毕,不然main线程就会卡在这里等消息
        ids.push(rx.recv());
    }
    
    for child in children {
         //关于JoinHandle详细说明下面有链接
        //`child 是 JoinHandle 类型;child.join的作用是:保证每个JoinHandel所关联的线程在每个child.join执行的时候是执行完毕的`,不然当前main线程就会等待JoinHandel所关联线程执行完毕
    //同时join方法会返回Result类型,从这个类型中我们可以判断我们给子线程的任务是否执行成功,如果执行成功也可获取其返回值;
      //我的疑惑是:: 当代码执行到这个for循环的时候就说明前面的每一个线程已经正确执行完毕,前一个for循环可以保证执行完毕这一点, 而 我们派发给每个线程的任务只是 简单的发一条消息,这个任务出错,或carsh,或panic 可能性很小; 那么这里我们是否可以省略掉 当前这个for循环中的代码?
        child.join().expect("oops! the child thread panicked");
    }

    println!("{:?}", ids);
}

std::thread::spawn
std::sync::mpsc::channel send
std::thread::JoinHandle,

扩展

  • channel 中 send方法什么时候会出错? 这些错误可以在 JoinHandle的Join方法中捕获吗?
  • Rust中 OS线程的任务中如果有加锁的情况,可是这个任务出错了,那么需要在 JoinHandle的Join方法中捕获这个错误并解锁吗?
  • OS线程的任务不会panic,或者抛异常,那么这个线程肯定回执行成功?
  • 多线程安全还要注意哪些方面?

期望

  • 可以回答你所熟悉的关于多线程安全的其他一些内容。
  • 希望有理有据,尽可能给出参考链接,或者其他有说服力的资料, 感谢在先

Refs

Rust By Example - Channels

阅读 2.1k
1 个回答

接受到消息并不能保证线程已经退出了,只说明对应线程的 send 已经调用了。所以要 join 保证线程也结束了。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题