Rust线程间通信通讯channel的理解和使用

news2024/10/12 20:24:11

Channel允许在Rust中创建一个消息传递渠道,它返回一个元组结构体,其中包含发送和接收端。发送端用于向通道发送数据,而接收端则用于从通道接收数据。不能使用可变变量的方式,线程外面修改了可变变量的值,线程里面是拿不到最新的值的。

不可用方式

is_loading在主线程里面,通过参数传递给子线程,等待5秒之后,主线程里面的is_loading修改为false,但是子线程里面获取不到最新的值,是由于所有权和线程安全性的限制所导致的。所以会导致子线程一直运行:

use std::io::{self, Write};
use std::thread;
use std::time::Duration;

fn main() {
    let mut is_loading = true;
    // creat close package must use move to get is_loading
    thread::spawn(move || { sub_loading(&is_loading) });
    // sleep 3 second then stop loading
    thread::sleep(Duration::from_secs(5));
    // set loading stop
    is_loading = false;
    loop {
        thread::sleep(Duration::from_millis(250));
        print!("build done");
    }
}


fn sub_loading(flag: &bool) {
    let loading_chars = vec!['-', '\\', '|', '/'];
    let mut index = 0;
    // cant get newe value
    while *flag {
        print!("\rLoading {} {} ", flag, loading_chars[index]);
        io::stdout().flush().unwrap();
        index = (index + 1) % loading_chars.len();
        thread::sleep(Duration::from_millis(250));
    }
}

Channel通道说明

每个channel由两部分组成:发送端(Sender)和接收端(Receiver)。  发送端用于向channel发送消息,而接收端则用于接收这些消息。这种机制允许线程之间的安全通信,避免了共享内存的复杂性和潜在的数据竞争问题。 (通过通信来共享内存,而非通过共享内存来通信)  Rust的channel为线程间通信提供了一种安全、简单的方式,是构建并发应用的基础工具之一。channel是Rust标准库的一部分,自Rust 1.0版本以来就包含了这个功能。随着Rust语言和标准库的发展,channel的实现和API可能会有所改进,但其基本概念和用法保持一致。

基本步骤如下:

创建: 使用std::sync::mpsc::channel()函数创建一个新的channel,这个函数返回一个包含发送端(Sender)和接收端(Receiver)的元组。

发送: 使用发送端的send方法发送消息。send方法接受一个消息值,如果接收端已经被丢弃,会返回一个错误。

接收: 使用接收端的recv方法接收消息。recv会阻塞当前线程直到一个消息可用,或者channel被关闭。

最简单的一个使用demo:

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

fn main() {
    // 创建一个通道
    let (sender, receiver) = mpsc::channel();

    // 在主线程中发送数据
    let data = 42;
    sender.send(data).unwrap();

    // 创建子线程来接收数据
    let child_thread = thread::spawn(move || {
        let received_data = receiver.recv().unwrap();
        println!("Received data in child thread: {}", received_data);
    });

    child_thread.join().unwrap();
}

在Rust中,如果你想停止一个由thread::spawn创建的线程,你不能直接停止它,因为Rust没有提供直接的方式来停止线程。但是,你可以通过通信来请求线程优雅地停止运行。

如果想使用一种方式,让主线程控制子线程停止,就可以使用channel通道:

use std::sync::mpsc;
use std::thread;
use std::time::Duration;

fn main() {
    let (tx, rx) = mpsc::channel();
    let child_thread = thread::spawn(move || {
        loop {
            // 检查是否收到停止信号
            if let Ok(_) = rx.try_recv() {
                println!("线程收到停止信号,正在退出...");
                break; // 退出循环,线程结束
            }
            // 执行线程的工作
            println!("sub child thread is ruing");
            // 为了演示,让线程休眠一会儿
            thread::sleep(Duration::from_secs(1));
        }
        println!("线程已退出。");
    });
    // 主线程等6秒,模拟耗时操作
    thread::sleep(Duration::from_secs(6));
    // 假设6秒后需要停止线程
    tx.send(()).unwrap(); // 发送停止信号
    // 等待线程退出
    child_thread.join().unwrap();
}

运行结果:

注意事项

recv:方法是阻塞的,即 它会阻塞当前线程, 直到从通道中接收到消息。 线程在调用rx.recv().unwrap()时会阻塞 等待消息的到来。一旦主线程通过tx.send(msg).unwrap();发送了消息,主线程会接收到这个消息并继续执行,之后程序才会正常退出。

try_recv:尝试在不阻塞的情况下返回此接收器上的挂起值。此方法永远不会为了等待数据可用而阻止调用方。相反,它将始终立即返回,并可能选择通道上的挂起数据。这对于在决定阻塞接收器之前进行“乐观检查”非常有用。

recv_timeout:尝试在此接收器上等待一个值,如果相应的通道已挂断或达到最后期限,则返回错误。如果没有可用的数据,并且可能发送更多的数据,此函数将始终阻止当前线程。将消息发送到相应的发件人(或SyncSender)后,此收件人将唤醒并返回该消息。如果相应的Sender已断开连接,或者在该呼叫被阻止时断开了连接,则该呼叫将唤醒并返回Err,以指示在此信道上无法再接收到消息。但是,由于通道是缓冲的,因此在断开连接之前发送的消息仍将被正确接收。

recv_deadline:返回一个迭代器,该迭代器将阻止等待消息,但永远不要惊慌!当频道挂断时,它将返回None。

iter:返回一个迭代器,该迭代器将尝试生成所有挂起的值。如果没有更多挂起的值或通道已挂起,它将返回None。迭代器永远不会死机!或者通过等待值来阻止用户。

关于MPSC讲解

其中mpsc是Multi producer, Single consumer FIFO queue的缩写,即多生产者单消费者先入先出队列。Rust标准库提供的channel是MPSC(多生产者,单消费者)模型,这意味着可以有多个发送端(Sender)向同一个接收端(Receiver)发送消息。这种模式非常适用于工作队列模型,其中多个生产者线程生成任务,而单个消费者线程处理这些任务。

除了MPSC之外,还有如下几种模型:

SPSC(Single Producer Single Consumer): 单生产者单消费者。

SPMC(Single Producer Multiple Consumer): 单生产者多消费者。

MPSC(Multi Producer Single Consumer): 多生产者单消费者, Rust中标准的mpsc模型。

MPMC(Multi Producer Multi Consumer)*: 多生产者多消费者。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1567219.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

UE5启用SteamOS流程

一、安装OnlineSubsystemSteam插件 1、在UE里安装OnlineSubsystemSteam 2、设置默认开始地图 3、设置DefaultEngine.ini文件: 打开项目根目录/Config/DefaultEngine.ini文件 打开官网的配置说明 复制并粘贴到该文件中 4、设置运行模式 5、测试 确保Steam平台已…

云原生:应用敏捷,华为视角下的应用现代化

Gartner 也提出,到 2023 年,新应用新服务的数量将达到 5 亿,也即是说:“每个企业都正在成为软件企业”。据IDC 预测,到 2025 年三分之二的企业将成为多产的“软件企业”,每天都会发布软件版本。越来越多的企…

【HTML】简单制作一个动态3D正方体

目录 前言 开始 HTML部分 JS部分 CSS部分 效果图 总结 前言 无需多言,本文将详细介绍一段代码,具体内容如下: 开始 首先新建文件夹,创建两个文本文档,其中HTML的文件名改为[index.html],JS的文件名改…

基于Python深度学习的中文情感分析系统(V2.0)

博主介绍:✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅👇…

v3-admin-vite 改造自动路由,view页面自解释Meta

需求 v3-admin-vite是一款不错的后端管理模板,主要是pany一直都在维护,最近将后台管理也进行了升级,顺便完成一直没时间解决的小痛痒: 在不使用后端动态管理的情况下。我不希望单独维护一份路由定义,我希望页面是自解…

STM32的定时器中断Cubemx

STM32的定时器中断Cubemx 0.定时器简介1.配置时钟2.配置定时器3.创建工程4.补充源码 0.定时器简介 基本定时器功能: 16位向上、向下、向上/下自动装载计数器16位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为1~65535之间的任意…

BGP-(as-path-filter)

BGP-as-path-filter,缺省 as-path-filter,正则表达式,as-path过滤器,对于BGP的as-path属性实际上可以看成是一个包含空格的字符串。 特点:1、通过对BGP路由的as-path属性进行匹配达到对BGP路由的过滤。 2、在route-…

Vue组件封装重要知识点

一、什么是组件? Vue.js的一个核心思想是组件化。所谓组件化,就是把页面拆分成多个组件,每个组件依赖的CSS、JavaScript、模板、图片等资源放在一起开发和维护。组件是资源独立的,组件在系统内部可复用,组件和组件之间…

时序预测 | Matlab实现CPO-BiLSTM【24年新算法】冠豪猪优化双向长短期记忆神经网络时间序列预测

时序预测 | Matlab实现CPO-BiLSTM【24年新算法】冠豪猪优化双向长短期记忆神经网络时间序列预测 目录 时序预测 | Matlab实现CPO-BiLSTM【24年新算法】冠豪猪优化双向长短期记忆神经网络时间序列预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现CPO-BiLST…

目标检测——图像中提取文字

一、重要性及意义 图像提取文本,即光学字符识别(OCR)技术,在现代社会中的重要性和意义日益凸显。以下是关于图像提取文本的重要性和意义的几个关键方面: 信息获取的效率提升 快速处理大量文档:OCR技术可…

基于AI智能识别技术的智慧展览馆视频监管方案设计

一、建设背景 随着科技的不断进步和社会安全需求的日益增长,展览馆作为展示文化、艺术和科技成果的重要场所,其安全监控系统的智能化升级已成为当务之急。为此,旭帆科技(TSINGSEE青犀)基于视频智能分析技术推出了展览…

OpenHarmony实战:标准系统移植指南

本文描述了移植一块开发板的通用步骤,和具体芯片相关的详细移植过程无法在此一一列举。后续社区还会陆续发布开发板移植的实例供开发者参考。 定义开发板 本文以移植名为MyProduct的开发板为例讲解移植过程,假定MyProduct是MyProductVendor公司的开发板…

区间概率预测python|QR-CNN-BiLSTM+KDE分位数-卷积-双向长短期记忆神经网络-时间序列区间概率预测+核密度估计

区间预测python|QR-CNN-BiLSTMKDE分位数-卷积-双向长短期记忆神经网络-核密度估计-回归时间序列区间预测 模型输出展示: (图中是只设置了20次迭代的预测结果,宽度较宽,可自行修改迭代参数,获取更窄的预测区间) 注&am…

基于java实现的弹幕视频网站

开发语言:Java 框架:ssm 技术:JSP JDK版本:JDK1.8 服务器:tomcat7 数据库:mysql 5.7(一定要5.7版本) 数据库工具:Navicat11 开发软件:eclipse/myeclip…

mongoDB 优化(2)索引

执行计划 语法: db.collection_xxx_t.find({"param":"xxxxxxx"}).explain(executionStats) 感觉这篇文章写得很好,可以参考 MongoDB——索引(单索引,复合索引,索引创建、使用)_mongo …

【办公类-48-01】20240404每月电子屏台账汇总成docx(问卷星xlsx导入docx,每页20条)

作品展示——docx台账汇总,每页20条 背景需求: 近期上级要求“一屏一码”,幼儿园每个电子屏使用后都要进行开机、关机的记录。安全主任分配工作后,每个园区的每个电子屏都有专人负责登记。 为了便于每月末的台账提交(…

使用Java拓展本地开源大模型的网络搜索问答能力

背景 开源大模型通常不具备最新语料的问答能力。因此需要外部插件的拓展,目前主流的langChain框架已经集成了网络搜索的能力。但是作为一个倔强的Java程序员,还是想要用Java去实现。 注册SerpAPI Serpapi 提供了多种搜索引擎的搜索API接口。 访问 Ser…

华为服务器RAID配置教程 服务器硬盘故障处理帮助 浪潮RAID配置教程 磁盘阵列配置通用教程

前言(本文档持续更新) 本文主要记录服务器配置RAID(磁盘阵列)过程中存在的细节问题及官方文档无法解决的问题的解决方案 配置环境 华为 RH2288 v3服务器配置RAID组 如何快速配置 1.找到服务器品牌的阵列卡型号,找不到…

使用vuepress搭建个人的博客(一):基础构建

前言 vuepress是一个构建静态资源网站的库 地址:VuePress 一般来说,这个框架非常适合构建个人技术博客,你只需要把自己写好的markdown文档准备好,完成对应的配置就可以了 搭建 初始化和引入 创建文件夹press-blog npm初始化 npm init 引入包 npm install -D vuepress…

【大数据存储】实验六:Hive

Hive的安装和基本操作实验 一、实验目的 了解Hive的安装和基本操作 二、实验原理 Hive定义了一套自己的SQL,简称HQL,它与关系型数据库的SQL略有不同,但支持了绝大多数的语句如DDL、DML以及常见的聚合函数、连接查询、条件查询。 DDL操作…