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)*: 多生产者多消费者。
相关文章:

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

Vue3组件基础示例
组件是vue中最推崇的,也是最强大的功能之一,就是为了提高重用性,减少重复性的开发。 如何使用原生HTML方法实现组件化 在使用原生HTML开发时,我们也会遇到一些常见的功能、模块,那么如何在原生HTML中使用组件化呢&am…...

如何使用PL/SQL Developer工具导出clob字段的表?
1 准备测试数据 导出测试对象:表test_0102,others字段为clob类型 --创建中间表test_0101 create table test_0101( id number, name varchar2(20), others clob);--插入100条测试数据 beginfor i in 1..100 loopinsert into test_0101 values(i,i||_a,l…...

蓝桥杯刷题 深度优先搜索-[NewOJ P1158]N皇后(C++)
题目描述 n皇后问题:n 个皇后放置在 nn 的棋盘上,并且使皇后彼此之间不能相互攻击。 上面布局用序列2 4 6 1 3 5表示,第i个数字表示第i行皇后放的列号。 按照这种格式输出前3个解,并统计总解数。 输入格式 输入一个正整数n&a…...

python实例2.2:编写一个装饰器,计算任何一个函数执行的时间(详解及其知识点拓展)
目录 一、编写一个装饰器,计算任何一个函数执行的时间 二、装饰器详解,及其用法举例...

Jenkins 持续集成 【CICD】
持续集成 (Continuous integration,简称CI) 持续集成是一种开发实践,它倡导团队成员频繁的集成他们的工作,每次集成都通过自动化构建(包括编译、构建、打包、部署、自动化测试)来验证ÿ…...

【CHI】(十二)Memory Tagging
目录 1. Introduction 2. Message extensions 3. Tag coherency 4. Read transaction rules 4.1 TagOp values 4.2 Permitted initial MTE tag states 5. Write transactions 5.1 Permitted TagOp values 5.2 TagOp, TU, and tags relationship 6. Dataless transact…...

Vue - 你知道Vue组件之间是如何进行数据传递的吗
难度级别:中级及以上 提问概率:85% 这道题还可以理解为Vue组件之间的数据是如何进行共享的,也可以理解为组件之间是如何通信的,很多人叫法不同,但都是说的同一个意思。我们知道,在Vue单页面应用项目中,所有的组件都是被嵌套在App.vue内…...

IP网络对讲广播系统审计
前言 这个系统是前两年在一个内网遇到的,当时顺手试了一个admin登陆之后再没有然后了,最近发现有大佬分享关于这个系统的漏洞,于是就把自己当初看的几个漏洞分享一下,系统比较简单,漏洞点很多,不要做坏事哦…...

蓝桥杯刷题--python38
197. 阶乘分解 - AcWing题库 def init(n): for i in range(2,n1): if not st[i]:primes.append(i) j0 while primes[j]*i<n: st[i*primes[j]]1 if i%primes[j]0: break j1 nint(input(…...

【LeetCode热题100】33. 搜索旋转排序数组(二分)
一.题目要求 整数数组 nums 按升序排列,数组中的值 互不相同 。 在传递给函数之前,nums 在预先未知的某个下标 k(0 < k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], …...

基于Leaflet.js的Marker闪烁特效的实现-模拟预警
目录 前言 一、闪烁组件 1、关于leaflet-icon-pulse 2、 使用leaflet-icon-pulse 3、方法及参数简介 二、闪烁实例开发 1、创建网页 2、Marker闪烁设置 3、实际效果 三、总结 前言 在一些地质灾害或者应急情况当中,或者热门预测当中。我们需要基于时空位置来…...

Vue-05
v-model 应用于其他表单元素 常见的表单元素都可以用v-model绑定关联 → 快速获取或设置表单元素的值 它会根据控件类型自动选取正确的方法来更新元素 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name…...

Mongodb中一个小巧的数据更新命令$inc
学习mongodb,体会mongodb的每一个使用细节,欢迎阅读威赞的文章。这是威赞发布的第55篇mongodb技术文章,欢迎浏览本专栏威赞发布的其他文章。 $inc是一个很小巧的命令。说它小巧,一个是因为短,只有三个字符。另一个是说…...

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

STM32一个地址未对齐引起的 HardFault 异常
1. 概述 客户在使用 STM32G070 的时候,KEIL MDK 为编译工具,当编译优化选项设置为Level0 的时候,程序会出现 Hard Fault 异常,而当编译优化选项设置为 Level1 的时候,则程序运行正常。表面上看,这似乎是 K…...

spring事务那些事
实际工作中还会面临千奇百怪的问题,看下面返个例子(注意MySql数据库测试): //1.hello1Service 调用 hello2Service Transactional(propagation Propagation.REQUIRED,rollbackFor Exception.class) public void doUpdate() {//…...

设计模式深度解析:AI大模型下的策略模式与模板方法模式对比解析
🌈 个人主页:danci_ 🔥 系列专栏:《设计模式》《MYSQL应用》 💪🏻 制定明确可量化的目标,坚持默默的做事。 策略模式与模板方法模式对比解析 文章目录 🌟引言🌟Part 1:…...

贪婪算法python实现
贪婪算法(Greedy Algorithm)是一种解决问题的策略,它基于一种贪心的思想:在每一步选择中都采取当前状态下最好或最优的选择,从而希望最终能够得到全局最优解。 其核心思想可以简单概括为“当前局部最优选择”ÿ…...

(一)基于IDEA的JAVA基础12
一维数组 为什么使用数组: 当我们需要存储一系列数据的时候,就需要用到数组,如果不使用数组,我们就要需要一个一个的去声明变量,这样浪费内存空间,同时效率低下。 什么是数组: 数组本身就是一个变量,只…...

vue3中封装table表格
封装实例useTable import {ref } from vue export function useTable(api) {const data = ref([])const refre...

【Redis】Redis的使用
登录redis [roottest2 ~]# redis-cli 127.0.0.1:6379> 或[roottest2 ~]# redis-cli -h 192.168.67.12 -p 6379 192.168.67.12:6379> redis-benchmark 测试工具 redis-benchmark 是官方自带的Redis性能测试工具,可以有效的测试Redis服务的性能 基本的测试语…...
【机器学习300问】60、图像分类任务中,训练数据不足会带来什么问题?如何缓解图像数据不足带来的问题?
在机器学习中,绝大部分模型都需要大量的数据进行训练和学习(包括有监督学习和无监督学习),然而在实际应用中经常会遇到训练数据不足的问题。就比如图像分类这样的计算机视觉任务,确实依赖于大规模且多样化的训练数据以…...

鸿蒙内核源码分析 (内存管理篇) | 虚拟内存全景图是怎样的
初始化整个内存 OsSysMemInitOsMainmain从 main() 跟踪可看内存部分初始化是在 OsSysMemInit() 中完成的。 UINT32 OsSysMemInit(VOID) {STATUS_T ret;OsKSpaceInit();//内核空间初始化ret OsKHeapInit(OS_KHEAP_BLOCK_SIZE);// 内核动态内存初始化 512K if (ret ! LOS_OK…...

基于深度学习的电动自行车头盔佩戴检测系统
文章目录 1. 文档说明2. 运行环境说明2.1 硬件配置2.2 软件配置2.3 程序依赖库 3. 基本环境配置3.1 软件安装3.1.1 集成开发环境安装与配置3.1.2 数据库安装与配置3.1.3 编程语言安装3.1.4 CUDA和cuDNN安装与配置3.1.5 机器学习库安装 3.2 依赖库安装 4. 运行程序资源下载地 1.…...

GO - 泛型编程
go - 泛型编程 介绍 泛型即开发过程中编写适用于所有类型的模板,只有在具体使用的时候才能确定其真正的类型。随着Go 1.18版本的发布,泛型正式成为了Go语言的一部分。 在编写代码时,我们经常会遇到需要处理不同类型的数据的情况。传统上&am…...

TouchableOpacity和TouchableWithoutFeedback区别
TouchableOpacity和TouchableWithoutFeedback都是React Native中定义的可触摸组件,但它们之间有一些区别: 点击效果:TouchableOpacity在被按下时会有一个透明度变化的点击效果,而TouchableWithoutFeedback则没有点击效果。 子组…...

MySQL EXISTS 语句和IN语句有啥区别
在 MySQL 中,EXISTS 和 IN 是用于子查询的两种不同方式,它们有一些区别: 1. **IN 语句**: - IN 子句用于在 WHERE 子句中指定多个值,并检查主查询中的某个列是否在子查询返回的结果集中。 - IN 子句适用于子查询…...

Java集合体系面试题
1. Java中有哪些主要的集合接口? 答案:Java中主要的集合接口有Collection、List、Set、Queue和Map。 2. 请解释List和Set之间的主要区别。 答案:List和Set的主要区别在于元素的顺序和唯一性。List是有序的集合,允许存储重复的元…...

React-2-useState-获取DOM-组件通信
一.useState useState 是一个 React Hook(函数),它允许我们向组件添加一个状态变量, 从而控制影响组件的渲染结果 本质:和普通JS变量不同的是,状态变量一旦发生变化组件的视图UI也会跟着变化**(数据驱动视…...