【问题标题】:How do the channels work in Rust By Example?通道在 Rust By Example 中是如何工作的?
【发布时间】:2016-12-21 19:34:47
【问题描述】:

channel chapter of Rust by Example 的输出让我很困惑:

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

使用默认的NTHREADS = 3,我得到以下输出:

thread 2 finished
thread 1 finished
[Ok(2), Ok(1), Ok(0)]

为什么for 循环中的println!("thread {} finished", id); 以相反的顺序打印? 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)]

打印顺序让我更加困惑,线程 0 总是丢失。这个例子怎么解释?

我在不同的计算机上尝试过,得到了相同的结果。

【问题讨论】:

  • NTHREADS = 8 我看到 线程 9 完成。关于这是怎么发生的任何想法?代码显然无法处理简单计数这一事实似乎是一个更糟糕的问题。
  • 您是否给出了设置 NTHREADS=10 而不是 8 的输出?

标签: multithreading rust channels


【解决方案1】:

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

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

主线程在退出之前等待子线程完成。缺少打印的原因是最后一个子线程将值发送到通道,主线程读取它(结束for 循环),然后程序退出。线程从来没有机会将其打印出来。

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

每种情况的可能性或多或少取决于 CPU 或操作系统的数量,但两者都是正确程序运行。

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

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

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

thread 2 finished
thread 1 finished
[Ok(2), Ok(1), Ok(0)]
thread 0 finished

这四行以任何顺序出现也是有效的:没有理由让任何子线程在主线程打印之前或之后打印。

【讨论】:

  • 谢谢,很有帮助!
猜你喜欢
  • 2013-08-20
  • 2011-11-18
  • 2014-10-22
  • 2015-12-08
  • 2021-08-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-03
相关资源
最近更新 更多