Rust使用Channel实现跨线程传递数据

 更新时间:2023年12月07日 10:17:24   作者:一诺滚雪球  
消息传递是一种很流行且能保证安全并发的技术,Rust也提供了一种基于消息传递的并发方式,在rust里使用标准库提供的Channel来实现,下面我们就来学习一下如何使用Channel实现跨线程传递数据吧

1. 概述

消息传递是一种很流行且能保证安全并发的技术,在这种机制里线程(或Actor)通过彼此发送消息(数据)来进行通信。Go语言中有一句名言:“不要用共享内存来通信,要用通信来共享内存”,Go语言这种并发机制就体现了这个思想。

Rust也提供了一种基于消息传递的并发方式,在rust里使用标准库提供的Channel来实现。Channel包含发送端和接收端,我们可以通过调用发送端的方法来发送数据,接收端会检查和接收到达的数据。如果发送端和接收端的任意一端被丢弃了,那么Channel就关闭了。

2. 使用Channel

2.1 在不同线程之间创建和接收数据

使用mpsc::channel函数来创建Channel,mpsc表示multiple producer, singer consumer(多个生产者、一个消费者),即有多个发送端,但只有一个接收端。调用该函数将返回一个元组,元组里的元素分别是发送端、接收端。

如下示例代码:

use std::sync::mpsc;
use std::thread;

fn main() {
    let (tx, rx) = mpsc::channel();

    thread::spawn(move || {
        let val = String::from("hi");
        tx.send(val).unwrap();
    });

    let received = rx.recv().unwrap();
    println!("Got: {}", received);
}

消费者的recv方法一直会阻塞当前线程,直到接收到消息为止。

2.2 发送端的send方法

该方法的参数为想要发送的数据,返回值为Result<T, E>,如果有问题(例如接收端已经被丢弃),将返回一个错误。

2.3 接收端的方法

recv方法阻止当前线程执行,直到Channel中有值被送来。一旦收到值,就会返回Result<T>,所有这个管道的所有发送端都关闭了,就会收到一个错误。

try_recv方法不会阻塞当前的线程,如果有数据到达,返回OK,里面包含着数据,否则返回错误。我们通常会使用循环来检查try_recv的结果,如果消息还没有来,我们也可以执行其他的操作。

2.4 channel和所有权转移

所有权先消息传递中非常重要,能帮你补全编写安全、并发的代码。

我们先看以下的示例代码:

use std::sync::mpsc;
use std::thread;

fn main() {
    let (tx, rx) = mpsc::channel();

    thread::spawn(move || {
        let val = String::from("hi");
        tx.send(val).unwrap();

        // 下面一行代码将会报错,因为所有权已经被转移
        println("val is {}", val);
    });

    let received = rx.recv().unwrap();
    println!("Got: {}", received);
}

在上面的示例代码中,借用了已移动的值,因此会发生编译错误。所以所有权机制会帮助我们编写编写安全、并发的代码。

2.5 发送多个值

我们通过发送多个值,就可以看到接收者在等待的过程。

如下示例代码:

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

fn main() {
    let (tx, rx) = mpsc::channel();

    thread::spawn(move || {
        let vals = vec![
            String::from("hi"),
            String::from("from"),
            String::from("the"),
            String::from("thread"),
        ];

        // 循环分别发送四个字符串
        for val in vals {
            tx.send(val).unwrap();
            thread::sleep(Duration::from_millis(1000));
        }
    });

    // 我们把接收端当作迭代器来使用,这样就不需要显式调用recv方法
    for received in rx {
        println!("Got: {}", received);
    }
}

运行以上的代码,我们将看到接收端在等待消息的过程。

2.6 通过克隆创建多个发送者

通过调用mpsc::Sender::clone函数可以克隆发送者。

如下示例代码:

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

fn main() {
    let (tx, rx) = mpsc::channel();

    let tx1 = mpsc::Sender::clone(&tx);

    thread::spawn(move || {
        let vals = vec![
            String::from("1: hi"),
            String::from("1: from"),
            String::from("1: the"),
            String::from("1: thread"),
        ];

        // 循环分别发送四个字符串
        for val in vals {
            tx1.send(val).unwrap();
            thread::sleep(Duration::from_millis(200));
        }
    });

    thread::spawn(move || {
        let vals = vec![
            String::from("hi"),
            String::from("from"),
            String::from("the"),
            String::from("thread"),
        ];

        // 循环分别发送四个字符串
        for val in vals {
            tx.send(val).unwrap();
            thread::sleep(Duration::from_millis(200));
        }
    });

    // 我们把接收端当作迭代器来使用,这样就不需要显式调用recv方法
    for received in rx {
        println!("Got: {}", received);
    }
}

在以上的示例代码中,我们通过两个子线程由两个发送者来发数据。并在主线程中使用接收者接收数据,可以通过程序运行结果看到由两个发送者发送的数据被交替输出。

到此这篇关于Rust使用Channel实现跨线程传递数据的文章就介绍到这了,更多相关Rust Channel跨线程传递数据内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Rust捕获全局panic并记录进程退出日志的方法

    Rust捕获全局panic并记录进程退出日志的方法

    本文提供了捕获全局panic并记录进程退出日志的方法,首先使用 panic::set_hook 注册异常处理及panic 触发异常,结合实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧
    2024-04-04
  • 详解Rust调用tree-sitter支持自定义语言解析

    详解Rust调用tree-sitter支持自定义语言解析

    使用Rust语言结合tree-sitter库解析自定义语言需要定义语法、生成C解析器,并在Rust项目中集成,具体步骤包括创建grammar.js定义语法,使用tree-sitter-cli工具生成C解析器,以及在Rust项目中编写代码调用解析器,这一过程涉及到对tree-sitter的深入理解和Rust语言的应用技巧
    2024-09-09
  • 最新Rust错误处理简介

    最新Rust错误处理简介

    Rust并不像C++一样使用try catch的异常机制来进行错误处理,他将错误分为可恢复错误和不可恢复错误两类,主要使用panic!宏和Result<T,E>类型来进行错误处理,这篇文章主要介绍了Rust错误处理简介,需要的朋友可以参考下
    2022-11-11
  • 详解rust 自动化测试、迭代器与闭包、智能指针、无畏并发

    详解rust 自动化测试、迭代器与闭包、智能指针、无畏并发

    这篇文章主要介绍了rust 自动化测试、迭代器与闭包、智能指针、无畏并发,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-11-11
  • 深入了解Rust 结构体的使用

    深入了解Rust 结构体的使用

    结构体是一种自定义的数据类型,它允许我们将多个不同的类型组合成一个整体。下面我们就来学习如何定义和使用结构体,并对比元组与结构体之间的异同,需要的可以参考一下
    2022-11-11
  • Rust实现一个表达式Parser小结

    Rust实现一个表达式Parser小结

    这篇文章主要为大家介绍了Rust实现一个表达式Parser小结,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-11-11
  • rust语言基础pub关键字及Some语法示例

    rust语言基础pub关键字及Some语法示例

    这篇文章主要为大家介绍了rust语言基础pub关键字及Some语法示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-07-07
  • rust多样化错误处理(从零学习)

    rust多样化错误处理(从零学习)

    一个优秀的项目,错误处理的优雅性是至关重要的,而rust,anyhow creat是绕不过去的一个,今天我们来研究下,怎么使用它,帮助我们写出更优雅的代码,有需要的朋友可以借鉴参考下,希望能够有所帮助
    2023-11-11
  • 使用systemd部署r-nacos的操作方法

    使用systemd部署r-nacos的操作方法

    r-nacos是一个用rust实现的nacos服务,我们用它平替java nacos以降低服务占用内存,提升服务的稳定性,这篇文章主要介绍了使用systemd部署r-nacos,需要的朋友可以参考下
    2024-03-03
  • 深入了解Rust的生命周期

    深入了解Rust的生命周期

    生命周期指的是引用保持有效的作用域,Rust 的每个引用都有自己的生命周期。本文将通过示例和大家详细说说Rust的生命周期,需要的可以参考一下
    2022-11-11

最新评论