如何以惯用方式在线程间传递数据?

10

我希望在单独的线程中进行一些计算,然后从主线程恢复数据。在Rust中传递数据的规范方式有哪些?

fn main() {
    let handle = std::thread::spawn(|| {
        // I want to send this to the main thread:
        String::from("Hello world!")
    });

    // How to recover the data from the other thread?

    handle.join().unwrap();
}
2个回答

18

有很多方法可以在线程之间发送数据,但没有明确的“最佳”解决方案。这取决于您的情况。


仅使用thread::join

许多人不知道只使用thread API 就可以非常容易地发送数据,但只能发送两次:一次发送到新线程,一次发送回来。

use std::thread;

let data_in = String::from("lots of data");
let handle = thread::spawn(move || {
    println!("{}", data_in);  // we can use the data here!

    let data_out = heavy_compuations();
    data_out // <-- simply return the data from the closure
});

let data_out = handle.join().expect("thread panicked :(");
println!("{}", data_out);  // we can use the data generated in the thread here!

(Playground)

对于只被用来完成某一个特定任务的线程,这非常有用。请注意,在闭包之前加上 move 关键字,以确保所有引用的变量都被移入了闭包(随后就会被移至另一线程)。


std 中的通道

标准库中提供了一个 multi producer single consumer 通道,位于 std::sync::mpsc 中。 你可以通过通道发送任意数量的值,因此它可以在更多情况下使用。以下是一个简单的示例:

use std::{
    sync::mpsc::channel,
    thread,
    time::Duration,
};

let (sender, receiver) = channel();
thread::spawn(move || {
    sender.send("heavy computation 1").expect("receiver hung up :(");
    thread::sleep(Duration::from_millis(500));
    sender.send("heavy computation 2").expect("receiver hung up :(");
});

let result1 = receiver.recv().unwrap();
let result2 = receiver.recv().unwrap();

(Playground)

当然,您可以创建另一个频道以提供双向通信。


crossbeam的更强大的通道

不幸的是,标准库目前仅提供了限于单个消费者(即Receiver无法克隆)的通道。要获得更强大的通道,您可能需要使用来自优秀的crossbeam库中的通道。它们的描述:

此crate是带有更多功能和更好性能的std::sync::mpsc替代品。

特别地,它是mpmc(多个消费者!)通道。这提供了一种轻松地在多个线程之间共享工作的方式。例如:

use std::thread;

// You might want to use a bounded channel instead...
let (sender, receiver) = crossbeam_channel::unbounded();
for _ in 0..num_cpus::get() {
    let receiver = receiver.clone();  // clone for this thread
    thread::spawn(move || {
        for job in receiver {
            // process job
        }
    });
}

// Generate jobs
for x in 0..10_000 {
    sender.send(x).expect("all threads hung up :(");
}

(游乐场)

再次,添加另一个通道允许您将结果发送回主线程。


其他方法

有很多其他的箱子可以提供在线程之间发送数据的其他方式。太多了,无法在此列出。

请注意,通过Mutex原子、无锁数据结构和许多其他方式进行数据的 发送 不是在线程之间通信的唯一方式。共享 数据也是一种可能性。这在概念上非常不同。根据情况,发送或共享数据是描述跨线程通信的更好方式。


3

惯用的方法是使用通道。它在概念上类似于单向隧道:您将某些东西放入一端,然后从另一端出来。

use std::sync::mpsc::channel;

fn main() {
    let (sender, receiver) = channel();

    let handle = std::thread::spawn(move || {
        sender.send(String::from("Hello world!")).unwrap();
    });

    let data = receiver.recv().unwrap();
    println!("Got {:?}", data);

    handle.join().unwrap();
}

当接收器被丢弃时,该通道将不再起作用。

恢复数据的主要方式有三种:


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接