如何通过Rust中的通道工作?

asd*_*fle 4 multithreading channels rust

我很困惑Rust示例通道章节的输出:

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();

    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
        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);
        });
    }

    // 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 no messages available
        ids.push(rx.recv());
    }

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

默认情况下NTHREADS = 3,我得到以下输出:

thread 2 finished
thread 1 finished
[Ok(2), Ok(1), Ok(0)]
Run Code Online (Sandbox Code Playgroud)

为什么println!("thread {} finished", id);for环路以相反的顺序打印?而在什么地方thread 0 finished去了?

当我改变时NTHREADS = 8,发生了一些更神秘的事情:

thread 6 finished
thread 7 finished
thread 8 finished
thread 9 finished
thread 5 finished
thread 4 finished
thread 3 finished
thread 2 finished
thread 1 finished
[Ok(6), Ok(7), Ok(8), Ok(9), Ok(5), Ok(4), Ok(3), Ok(2), Ok(1), Ok(0)]
Run Code Online (Sandbox Code Playgroud)

打印命令使我更加困惑,线程0总是丢失.如何解释这个例子?

我在不同的计算机上尝试了这个并获得了相同的结果.

She*_*ter 8

线程没有保证顺序或它们之间的任何协调,因此它们将以任意顺序执行并将结果发送到通道.这就是整点 - 如果它们是独立的,你可以使用多个线程.

主线程N从通道中提取值,将它们放入a Vec,打印Vec和退出.

主线程并没有等待子线程退出之前完成.丢失的打印由最后一个将线程值发送到通道的子线程解释,主线程读取它(结束for循环),然后程序退出.线程永远不可能打印出来完成.

在主线程恢复和退出之前,最后一个线程也有可能运行并打印出来.

根据CPU或OS的数量,每种方案可能或多或少可能,但两者都是程序的正确运行.

修改为等待线程的代码版本显示不同的输出:

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 handles: Vec<_> = (0..NTHREADS).map(|id| {
        // The sender endpoint can be copied
        let thread_tx = tx.clone();

        // Each thread will send its id via the channel
        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);
        })
    }).collect();

    // 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 no messages available
        ids.push(rx.recv());
    }

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

    // Wait for threads to complete
    for handle in handles {
        handle.join().expect("Unable to join");
    }
}
Run Code Online (Sandbox Code Playgroud)

注意,在这种情况下,主线程最后一个线程退出之前打印:

thread 2 finished
thread 1 finished
[Ok(2), Ok(1), Ok(0)]
thread 0 finished
Run Code Online (Sandbox Code Playgroud)

这四行几乎可以任何顺序发生:在主线程打印之前或之后没有任何原因可以打印任何子线程.