当前位置: 首页 > news >正文

2311rust无畏并发.

原文

Rust无畏并发

Rust是为了解决两个麻烦问题:
1,如何安全系统编程
2,如何无畏并发

最初,这些问题似乎是无关的,但令惊讶的是,方法竟然是相同的:使Rust安全的相同工具也可帮助解决并发问题.

内存安全和并发错误,一般认为是代码在不应访问数据时访问数据.Rust依靠所有权为你静态检查.

内存安全,即可在无垃集时编程,也不必担心段错误,因为Rust会发现你的错误.

并发性,即可从(传递消息,共享状态,无锁,纯函数式)中选择,而Rust帮助你避免常见的陷阱.

以下是Rust中的并发性:

1,通道转移了发送消息的所有权,因此可从一个线程发送指针到另一个线程,而不必担心线程竞争.Rust通道强制隔离线程.这里

2,知道它保护了哪些数据,且Rust保证,只有在持有锁时,才能访问数据.而不会共享状态.在Rust中强制"锁定数据,而不是代码".

3,在多线程之间,每种数据类型都知道它是否可安全发送或访问,且Rust强制,即使对无锁数据结构,也无数据竞争.线安不仅是文档;也是规则.

4,甚至可在线程共享栈帧这里,Rust静态地确保,在其他线程使用它们时,这些帧活跃.即使是最大胆的共享形式,在Rust中也能保证安全.

这些好处都来自Rust所有权模型,事实上,锁,通道,无锁数据结构等都是在库中而不是核心语言中定义的.
Rust并发方法是开放的:新库可带有新的范式并抓新的错误,只需添加使用Rust所有权功能的API.

背景:所有权

Rust中,每个值都有个"物主域",传递或返回值表明从旧所有权转移("移动")到新域.在结束时,此时自动析构仍拥有的值.
看看简单示例.假设创建一个向量并推送一些元素到它上面:

fn make_vec() {let mut vec = Vec::new();//归`make_vec`的域所有vec.push(0);vec.push(1);//域结束,析构`"vec"`
}

创建值的域最初也拥有它.此时,make_vec的主体是vec的物主域.物主可用vec干活.
在域结束时,仍归域所有vec,因此会自动释放.
如果返回或传递向量,会更有趣:

fn make_vec() -> Vec<i32> {let mut vec = Vec::new();vec.push(0);vec.push(1);vec //转让`所有权`给调用者
}
fn print_vec(vec: Vec<i32>) {//`"vec"`参数是此域的一部分,因此归`"print_vec"`所有for i in vec.iter() {println!("{}", i)}//现在,释放`"vec"`
}
fn use_vec() {let vec = make_vec(); //取向量所有权,print_vec(vec);       //传递所有权给`"print_vec"`
}

现在,在make_vec域结束前,vec返回它来出域;不会析构它.然后,像use_vec此调用者获得向量所有权.
另一方面,print_vec函数带vec参数,由其调用者把向量的所有权转移给它.因为print_vec不会进一步转移所有权,因此在其域结束时,就析构向量.
一旦放弃所有权,就不能再使用该值.如,请考虑以下use_vec变体:

fn use_vec() {let vec = make_vec();  //取`VectorPass`所有权print_vec(vec);        //传递所有权给`"print_vec"`,for i in vec.iter() {  //继续使用`"vec"`println!("{}", i * 2)}
}

编译器说不再可用vec;已转移所有权.这非常好,因此时已释放了向量!避免了灾难.

借贷

目前,并不满意,因为无意print_vec析构向量.真正想要的是临时授予print_vec访问向量,然后继续使用向量.

这就要靠借贷了.如果有权访问Rust中的某个值,可把该权限借给调用函数.Rust检查这些生命期不会超过被借对象.
借用一个值,可用&符号引用它(一个指针):

fn print_vec(vec: &Vec<i32>) {//`"vec"`参数是`此域`借用的for i in vec.iter() {println!("{}", i)}//现在,借期结束了
}
fn use_vec() {let vec = make_vec();  //取向量的所有权print_vec(&vec);       //借出`"print_vec"`权限for i in vec.iter() {  //继续使用`"vec"`println!("{}", i * 2)}//在此析构`VEC`
}

现在print_vec接受向量引用,use_vec通过编写&vec来借出向量.因为是临时的,use_vec保留了向量所有权;

可在调用print_vec返回后继续使用它.

每个引用有限域内有效,编译器自动确定该域.有两种引用形式:

1,不变引用&T,允许共享禁止改变.可同时有多个对同一值的&T引用,但当这些引用活动时,不能更改该值.
2,可变引用&mut T,允许改变不共享.如果存在对某个值的&mut T引用,则此时不能有其他活动引用,但可更改该值.

Rust在编译时检查这些规则;借用没有运行时成本.
为什么有两类引用?考虑此函数:

fn push_all(from: &Vec<i32>, to: &mut Vec<i32>) {for i in from.iter() {to.push(*i);}
}

此函数遍历向量的每个元素,把它推送另一个向量上.迭代器在当前和最终位置保持向量指针,挨个前进.
如果用相同向量,为两个参数调用此函数怎么办?

push_all(&vec, &mut vec)

这将是一场灾难!推送元素到向量上时,它偶尔要调整,分配大量新内存并复制进元素.迭代器会剩下旧内存指针的悬挂指针,导致内存不安全(段错误则更糟).

幸好,Rust确保每当可变借用活动时,其他借用都不会活动,从而产生以下消息:
错误:不能按可变借用"vec",因为它也按不变借用.

push_all(&vec, &mut vec);^~~

传递消息

并发编程有多种风格,特别简单方式是线程或参与者相互发送消息来通信的传递消息.
不通过共享内存交流;相反,通过交流共享内存.

Rust所有权使得很容易检查规则.考虑以下通道API(Rust标准库中的通道略有不同):

fn send<T: Send>(chan: &Channel<T>, t: T);
fn recv<T: Send>(chan: &Channel<T>) -> T;

通道在它们传输的数据类型(API<T:Send>部分)上是通用的.Send部分表明T必须是安全的,可在线程之间发送;
Vec<i32>Send.

Rust中一样,传递Tsend函数表明转移它的所有权.这一事实深远影响:即,下面代码生成编译器错误.

//假设`chan:Channel<Vec<i32>>`
let mut vec = Vec::new();
//做一些计算
send(&chan, vec);
print_vec(&vec);

在此,线程创建了一个向量,并发送它到另一个线程,然后继续使用它.当该线程继续运行时,接收向量线程可能会更改它,因此调用print_vec,可能会导致竞争,因此,导致释放后使用错误.

相反,在调用print_vec时,Rust编译器会生成错误消息:
错误:使用移动"vec"值.
避免了灾难.

锁,被动共享状态来通信的方式.
共享状态并发有个缺点.很容易忘记取锁,或在错误时间改变错误数据,导致灾难.

Rust的观点是:
然而,共享状态并发是基本编程风格,系统代码,最大性能及实现其他并发风格都需要它.
问题与意外共享状态有关.

无论使用有锁还是无锁技术,Rust旨在为你提供直接征服共享状态并发的工具.

Rust中,因为所有权,线程会自动相互"隔离".无论是拥有数据,还是可变借用数据,仅当线程可变权限时,才会写入.

总之,保证该线程是当时唯一有权限的线程.
请记住,不能同时有可变借用与其他借用.锁通过运行时同步提供相同的保证("互斥").这导致直接勾挂到Rust所有权系统的锁API.
如下是简化版本:

//创建新的互斥锁
fn mutex<T: Send>(t: T) -> Mutex<T>;
//取锁
fn lock<T: Send>(mutex: &Mutex<T>) -> MutexGuard<T>;
//访问受锁保护的数据
fn access<T: Send>(guard: &mut MutexGuard<T>) -> &mut T;

此锁API的不寻常点.
1,首先,在锁保护数据T类型上,互斥类型通用的.创建互斥锁时,转移该数据所有权到互斥锁中,立即放弃了所有权.(在首次创建时解锁).
2,稍后,你可锁(lock)阻止线程,直到获得.在析构MutexGuard时自动释放锁;没有单独的解锁(unlock)函数.
3,只能通过访问(access)函数访问锁,该函数把守卫可变借用转换为数据的可变借用(短期借用):

fn use_lock(mutex: &Mutex<Vec<i32>>) {//获得锁,拥有警卫;在域的其余部分持有锁let mut guard = lock(mutex);//通过可变借用`Guard`来访问数据let vec = access(&mut guard);//`vec`的类型为`"&mut Vec<i32>"`vec.push(3);//析构`"守卫"`时,会自动在此处释放锁
}

两个关键要素:
1,访问(access)返回的可变引用不能超过比它借用的MutexGuard.
2,仅当析构MutexGuard时,才会释放锁.

结果是Rust强制保证锁规则:除非持有锁,否则禁止访问受锁保护数据.否则生成编译器错误.如,考虑以下有缺陷的"重构":

fn use_lock(mutex: &Mutex<Vec<i32>>) {let vec = {//取锁let mut guard = lock(mutex);//试返回借用数据access(&mut guard)//在此析构`守卫`,释放了锁};//试访问锁外数据.vec.push(3);
}

Rust生成错误来说明问题:
错误:"guard"的生命期不够长

access(&mut guard)^~~~~

避免了灾难.

线安和"发送"

一般区分某些数据类型为"线安",而其他数据类型则不是.线安数据结构内部有足够同步,以便可同时安全地使用多线程.
如,Rust附带了两个来引用计数的"灵针":
1,Rc<T>通过正常读/写提供引用计数.它不是线安的.
2,Arc<T>通过原子操作提供引用计数.它是线安的.

Arc使用的硬件原子操作比Rc使用的普通操作更贵,因此使用Rc而不是Arc是有利的.另一方面,重点,永远不要从一个线程迁移Rc<T>到另一个线程,因为会导致破坏引用计数的竞争.

Rust中,世界分为两个数据类型:一个是Send,即可安全地从一个线程移动到另一个线程,其余是!Send(不安全).

如果某个类型的所有组件都是Send,则该类型也是Send,它涵盖了大多数类型.但是,某些基本类型不是线安的,因此也可按Send显式标记Arc等类型,对编译器说:相信我;已在此验证了必要的同步.

当然,ArcSend,而Rc不是.

可见,通道和互斥API仅适合发送(Send)数据.因为它们是跨越线程边界的数据点,因此它们也是Send强制点.

综上,Rust可自信地获得Rc和其他线程不安全类型的好处,因为,如果不小心试发送一个线程到另一个线程,Rust编译器会说:
无法安全地在线程之间发送"Rc<Vec<i32>>".
这避免了灾难.

共享栈:"scoped"

注意:这里提到的API是一个旧的API,已从标准库中移出.你可在横梁(scope()文档)和scoped_threadpool(scoped()文档)中找到等效的函数.

目前,所有模式都涉及在上创建,在线程共享的数据结构.但是,如果想启动一些线程来利用栈帧中的数据,则可能会很危险:

fn parent() {let mut vec = Vec::new();//填充向量thread::spawn(|| {print_vec(&vec)})
}

子线程接受vec引用,而vec保留在父线程的栈帧中.父线程退出时,会弹出栈帧,但子线程并不知道.哎呀!

为了排除该内存不安全,Rust的基本线程生成API如下:

fn spawn<F>(f: F) where F: 'static, ...

"静态约束"即,指在闭包禁止借用数据.即像上面此parent函数会生成错误:
错误:"vec"的生命期不够长.

基本上抓住了弹出父栈帧的可能性.避免了灾难.

还有另一个方法可保证安全性:直到子线程完成,确保父栈帧保持原位.这是分叉连接编程的模式,一般用于分而治之的并行算法.
Rust通过提供线程生成的"域"变体来支持它:

fn scoped<'a, F>(f: F) -> JoinGuard<'a> where F: 'a, ...

与上面的spawn接口有两个主要区别:
1,使用'a参数,而不是'static.
2,JoinGuard返回值.即,JoinGuard通过在其析构器隐式连接(如果尚未显式)来确保父线程加入(等待)其子线程.

JoinGuard中包含'a可确保JoinGuard无法逃脱闭包借用的数据的域.即,Rust保证在弹出子线程可能访问的栈帧前,父线程等待子线程完成.

因此,调整之前示例,可如下修复错误并满足编译器:

fn parent() {let mut vec = Vec::new();//填充向量let guard = thread::scoped(|| {print_vec(&vec)});//在此析构`守卫`,隐式合并
}

因此,在Rust中,可自由地把栈数据借用到子线程中,编译器确保检查是否有足够同步.

数据竞争

Rust使用所有权和借用来保证:
1,内存安全,无垃集.
2,无并发数据竞争.

相关文章:

2311rust无畏并发.

原文 Rust无畏并发 Rust是为了解决两个麻烦问题: 1,如何安全系统编程 2,如何无畏并发 最初,这些问题似乎是无关的,但令惊讶的是,方法竟然是相同的:使Rust安全的相同工具也可帮助解决并发问题. 内存安全和并发错误,一般认为是代码在不应访问数据时访问数据.Rust依靠所有权为…...

阿里云中的云服务器的ubuntu中的vim没有显示行号

没有行号&#xff1a; 在终端输入命令&#xff1a; vim ~/.vimrc set nu...

Golang 在 Mac、Linux、Windows 下如何交叉编译

Golang 支持交叉编译&#xff0c;在一个平台上生成另一个平台的可执行程序。 GOOS&#xff1a;目标平台的操作系统&#xff08;darwin、freebsd、linux、windows&#xff09; GOARCH&#xff1a;目标平台的体系架构&#xff08;386、amd64、arm&#xff09; 具体组合&#xf…...

如何写好一篇学术论文

目录 前言 1.标题和摘要 1.1标题 1.2摘要及关键词 1.2.1摘要 1.2.2关键词 2.正文 2.1引言 2.2问题建模 2.3研究方法及分析 2.4仿真&#xff08;伪代码&#xff09; 2.5实验结果及分析 2.6 总结 2.7延深 2.7.1图片处理 2.7.2审稿回复 2.7.3如何避免拒稿 2.7.4写…...

kubernetes资源监控

目录 一、资源限制 1、limitrange 2、ResourceQuota 二、metrics-server 三、图形化监控和代码行监控 1、dashboard 2、k9s 四、hpa 一、资源限制 Kubernetes采用request和limit两种限制类型来对资源进行分配。request(资源需求)&#xff1a;即运行Pod的节点必须满足运…...

Bitget Wallet:使用 Base 链购买 ETH 的简明教程

Base 链是一种 Layer 2&#xff08;L2&#xff09;公链&#xff0c;它可以为用户提供以太坊&#xff08;ETH&#xff09;代币&#xff0c;而 Bitget Wallet 是一款多功能加密货币钱包&#xff0c;支持 Base 链以及其他主要区块链。...

PostgreSQL简介及安装步骤

PostgreSQL简介 PostgreSQL是一款开源的关系型数据库管理系统&#xff0c;具有强大的扩展性、高度的可定制性和可靠的稳定性&#xff0c;因此在企业级应用和开发领域中得到了广泛的应用。本文将介绍PostgreSQL的基本概念以及在各种操作系统上的安装步骤。 安装步骤 1. Window…...

《安富莱嵌入式周报》第326期:航空航天级CANopen协议栈,开源USB PD电源和功耗分析,开源EtherCAT伺服驱动板,时序绘制软件,现代机器人设计

周报汇总地址&#xff1a;嵌入式周报 - uCOS & uCGUI & emWin & embOS & TouchGFX & ThreadX - 硬汉嵌入式论坛 - Powered by Discuz! 更新一期视频教程&#xff1a; BSP视频教程第28期&#xff1a;CANopen协议栈专题&#xff0c;CANopen主从机组网实战&a…...

[Kettle] Excel输入

Excel文件采用表格的形式&#xff0c;数据显示直观&#xff0c;操作方便 Excel文件采用工作表存储数据&#xff0c;一个文件有多张不同名称的工作表&#xff0c;分别存放相同字段或不同字段的数据 数据源 物理成绩(Kettle数据集2).xls https://download.csdn.net/download/H…...

vue3+ts 项目遇到的问题和bug

1.router中使用pinia报错 pinia.mjs:1709 Uncaught Error: [&#x1f34d;]: "getActivePinia()" was called but there was no active Pinia. Are you trying to use a store before calling "app.use(pinia)"? See https://pinia.vuejs.org/core-concep…...

【Linux】补充:进程管理之手动控制进程,以及计划任务

目录 一、手动启动进程 1、理解前台启动与后台启动 2、如何完成前台启动后台启动的切换 3、完成并行执行多个任务 4、结束进程 1、kill 2、killall 2、pkill 二、计划任务 1、at一次性计划任务 2、实操 2、周期性计划任务 1、关于设置周期性任务的配置文件以及格式…...

听说,工作能力强的项目经理都有这几个特征

大家好&#xff0c;我是老原。 很多项目经理每天忙忙碌碌&#xff0c;但是一看结果&#xff0c;团队业绩没有完成、人才没有培养起来、自己的管理水平和个人领导力也没有得到提升。 明明付出了很多时间和精力&#xff0c;结果却只收获了团队的抱怨&#xff0c;以及老板对你管…...

合并两个有序链表OJ

合并两个有序链表OJ 文章目录 合并两个有序链表OJ一、题目及要求二、思路分析三、代码实现 一、题目及要求 二、思路分析 其次&#xff0c;题目里说了新链表是通过拼接原来的结点形成的&#xff0c;所以说我们不需要开辟新的空间。 三、代码实现 if (list1 NULL) {return li…...

2023NOIP A层联测27 A.kotori

2023NOIP A层联测27 A.kotori 文章目录 2023NOIP A层联测27 A.kotori题目大意思路code 题目大意 琴里的飞船中有 n n n 个人&#xff0c;其中有 n − 1 n - 1 n−1 个通道&#xff0c;所以飞船的内部是一个树形结构。每个人从 1 − n 1-n 1−n 编号&#xff0c;编号越小代表…...

循环生成el-descriptions-item

0 后端返回数据格式 {"msg": "操作成功","code": 200,"data": {"id": 42,"contactInfo": [{"contactPerson": "张三","contactPhone": "13688888888"},{"contactP…...

【原创】java+swing+mysql爱心捐赠管理系统设计与实现

摘要&#xff1a; 爱心捐赠管理系统旨在管理和优化捐赠过程&#xff0c;提高效率&#xff0c;增强透明度&#xff0c;并鼓励更多的个人和企业参与公益捐赠&#xff0c;用户可以捐款或者捐物。本系统采用javaswing界面可视化技术&#xff0c;数据库使用mysql。 功能分析&#…...

【小技巧】WPS统计纯汉字(不计标点符号)

【小技巧】WPS统计纯汉字&#xff08;不计标点符号&#xff09; 首先&#xff0c;CtrlF打开查找页面&#xff1a; 选择“高级搜索”&#xff0c;然后勾选“使用通配符”&#xff0c;然后在“查找内容”后面输入&#xff1a;[一-﨩]。注意&#xff1a;一定要带“[]”和“-”且…...

【押题】24考研押题

数二选手来押24数一考研大题 1.大题必有级数。级数出在压轴题&#xff0c;考级数敛散性与数列极限的结合 2.数一倒数第二题65%考画不出图的三重积分&#xff0c;参考19年出法&#xff1b;35%考第一类曲面积分与空间解析几何的结合。大题不会考第二类线面积分 3.概率大题会考参数…...

前端设计模式

前端设计模式 &#x1f3a8; 设计模式是在软件开发中&#xff0c;针对常见问题的解决方案的经验总结。在前端开发中&#xff0c;设计模式可以帮助我们组织和管理代码&#xff0c;提高代码的可维护性和可扩展性。下面列举一些常见的前端设计模式&#xff1a; 1. 单例模式 (Sin…...

Tomcat的类加载器

详情可以参考&#xff1a;https://tomcat.apache.org/tomcat-10.1-doc/class-loader-howto.html 简要说明 Tomcat安装了多种类加载器&#xff0c;以便容器的不同部分、容器中的应用访问能够不同的类和资源。 在Java环境中&#xff0c;类加载器被组织为父-子树的形式。通常情况…...

OpenLayers 可视化之热力图

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 热力图&#xff08;Heatmap&#xff09;又叫热点图&#xff0c;是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...

使用VSCode开发Django指南

使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架&#xff0c;专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用&#xff0c;其中包含三个使用通用基本模板的页面。在此…...

java_网络服务相关_gateway_nacos_feign区别联系

1. spring-cloud-starter-gateway 作用&#xff1a;作为微服务架构的网关&#xff0c;统一入口&#xff0c;处理所有外部请求。 核心能力&#xff1a; 路由转发&#xff08;基于路径、服务名等&#xff09;过滤器&#xff08;鉴权、限流、日志、Header 处理&#xff09;支持负…...

Admin.Net中的消息通信SignalR解释

定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...

uniapp微信小程序视频实时流+pc端预览方案

方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度​WebSocket图片帧​定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐​RTMP推流​TRTC/即构SDK推流❌ 付费方案 &#xff08;部分有免费额度&#x…...

Swagger和OpenApi的前世今生

Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章&#xff0c;二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑&#xff1a; &#x1f504; 一、起源与初创期&#xff1a;Swagger的诞生&#xff08;2010-2014&#xff09; 核心…...

均衡后的SNRSINR

本文主要摘自参考文献中的前两篇&#xff0c;相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程&#xff0c;其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt​ 根发送天线&#xff0c; n r n_r nr​ 根接收天线的 MIMO 系…...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)

在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马&#xff08;服务器方面的&#xff09;的原理&#xff0c;连接&#xff0c;以及各种木马及连接工具的分享 文件木马&#xff1a;https://w…...

PAN/FPN

import torch import torch.nn as nn import torch.nn.functional as F import mathclass LowResQueryHighResKVAttention(nn.Module):"""方案 1: 低分辨率特征 (Query) 查询高分辨率特征 (Key, Value).输出分辨率与低分辨率输入相同。"""def __…...

免费数学几何作图web平台

光锐软件免费数学工具&#xff0c;maths,数学制图&#xff0c;数学作图&#xff0c;几何作图&#xff0c;几何&#xff0c;AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...