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

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_loadingthread::spawn(move || { sub_loading(&is_loading) });// sleep 3 second then stop loadingthread::sleep(Duration::from_secs(5));// set loading stopis_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 valuewhile *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.hqwc.cn/news/590104.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

某团App之mtgsig2.4算法分析

1.3mtgsig参数分析: a0:mtgsig版本号(这里分析2.4) a1:appkey(相同版本app此值固定) a3:Android设备版本 a4:时间戳 a5:加密的设备信息1 a6:固定值…

并发编程之线程池的应用以及一些小细节的详细解析

线程池在实际中的使用 实际开发中,最常用主要还是利用ThreadPoolExecutor自定义线程池,可以给出一些关键的参数来自定义。 在下面的代码中可以看到,该线程池的最大并行线程数是5,线程等候区(阻塞队列)是3,即…

利用sqoop实现sql表数据导入到Hadoop

1.在开发这创建好sql表后,开始执行下面步骤 2.sqoop的安装路径,我这里放在以下位置 3. 进入到option2脚本中,下面是脚本里的内容 下面四点要根据情况随时更改: 1>jdbc:mysql://node00:3306/数据库名 2>sid,sname->前…

循序渐进丨MogDB Ustore存储引擎剖析

背景 MogDB 数据库支持不同的存储引擎,其中行存引擎有Astore和Ustore,目前大部分客户场景使用的是Astore,也就是 Append Update(追加更新)模式。 Astore对于业务中的增、删以及HOT Update(即同一页面内更新…

智慧城市治理:构建全域覆盖的城市时空感知体系

TSINGSEE青犀AI算法中台是一款平台型产品,专注于提供各行业中小场景部署解决方案。平台具备接入广、性能强、支持跨平台、芯片国产化等特点,可提供丰富的视图接入能力和智能分析能力。 平台采用了多项IT高新技术,包括视频编解码技术、嵌入式…

Web 后台项目,权限如何定义、设置、使用:菜单权限、按钮权限 ts element-ui-Plus

Web 后台项目,权限如何定义、设置、使用:菜单权限、按钮权限 ts element-ui-Plus 做一个后台管理项目,里面需要用到权限管理。这里说一下权限定义的大概,代码不多,主要讲原理和如何实现它。 一、权限管理的原理 权限…

elementui 实现一个固定位置的Pagination(分页)组件

系列文章目录 一、elementui 导航菜单栏和Breadcrumb 面包屑关联 二、elementui 左侧导航菜单栏与main区域联动 三、elementui 中设置图片的高度并支持PC和手机自适应 四、 elementui 实现一个固定位置的Pagination(分页)组件 文章目录 系列文章目录…

酷开科技智慧AI让酷开系统大显身手!

时代的浪潮汹涌而至,人工智能作为技术革新和产业变革的重要引擎,正深刻地影响着各行各业。在科技的海洋中,AI技术正逐渐渗透到我们的日常生活中,为我们带来前所未有的便捷和智慧。酷开科技用技术探索智慧AI,别看它只是…

企业家见识、智慧与胸怀:超越知识、聪明与财富的核心价值​

一、引言 在商界的风云变幻中,企业家们不仅需要拥有丰富的知识和聪明才智,更需要具备远见卓识、深刻智慧和博大胸怀。正如某知名企业家所言:“企业家见识比知识重要,智慧比聪明重要,胸怀比财富重要。”,这…

从零开始:Flutter应用上架iOS的完整流程解析

引言 🚀 Flutter作为一种跨平台的移动应用程序开发框架,为开发者提供了便利,使他们能够通过单一的代码库构建出高性能、高保真度的应用程序,同时支持Android和iOS两个平台。然而,完成Flutter应用程序的开发只是第一步…

Spring Boot--文件上传和下载

文件上传和下载 前言文件上传1、以MultipartFile 接口流文件,流的名称需要和前台传过来的名称对应上2、获取到文件名称截取后缀3、为了放置文件名重复使用uuid来随机生成id后缀4、判断转存路径中是否有这个文件夹如果没有就创建5、将文件存储到转存的目录中 文件下载…

python的垃圾回收

引用计数器为主,标记清除和分代回收为辅 1 引用计数器 在python程序运行时,会根据数据类型的不同找到其对应的结构体,根据结构体中的字段来进行创建相关的数据,然后将对象添加到refchain双像链表中,每个对象中的ob_re…