首页 > 解决方案 > 在线程之间发送数据的惯用方式是什么?

问题描述

我想在一个单独的线程中做一些计算,然后从主线程中恢复数据。在 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();
}

标签: multithreadingrust

解决方案


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


只使用thread::join

许多人没有意识到您可以非常轻松地仅使用threadAPI 发送数据,但只能发送两次:一次到新线程,一次返回。

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!

游乐场

这对于刚刚为执行一项特定工作而生成的线程非常有用。注意move闭包之前的关键字,它确保所有引用的变量都被移动到闭包中(然后被移动到另一个线程)。


来自的频道std

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

游乐场

当然,您也可以创建另一个渠道来提供另一个方向的通信。


更强大的渠道crossbeam

不幸的是,标准库目前只提供仅限于单个消费者的渠道(即Receiver不能被克隆)。要获得更强大的频道,您可能想要使用来自 awesome crossbeamlibrary的频道。他们的描述:

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

游乐场

同样,添加另一个通道允许您将结果传达回主线程。


其他方法

还有很多其他 crate 提供了在线程之间发送数据的其他方式。太多了,这里就不一一列举了。

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


推荐阅读