Rust中的并发性:Sync 和 Send Traits

在并发的世界中,最常见的并发安全问题就是数据竞争,也就是两个线程同时对一个变量进行读写操作。但当你在 Safe Rust 中写出有数据竞争的代码时,编译器会直接拒绝编译。那么它是靠什么魔法做到的呢?

这就不得不谈 Send 和 Sync 这两个标记 trait 了,实现 Send 的类型可以在多线程间转移所有权,实现 Sync 的类型可以在多线程间共享引用。但它们内部都是没有任何方法声明以及方法体的,二者仅仅是作为一个类型约束的标记信息提供给编译器,帮助编译器拒绝线程不安全的代码。

定义:

pub unsafe auto trait Send { }

pub unsafe auto trait Sync { }

本文将深入探讨 SyncSend traits,了解为什么某些类型实现这些 traits,而另一些则没有,并讨论 Rust 中并发编程的最佳实践。

The Sync Trait

Sync trait 表示一个类型可以安全地被多个线程同时访问。这里的访问指的是只读共享安全。Rust 中几乎所有的原始类型都实现了 Sync trait

例如:

let x = 5; // i32 is Sync

i32 类型实现了 Sync ,所以在线程间共享 i32 值是安全的。

另一方面,提供内部可变性的类型(内部可变性指的是在拥有不可变引用的时候,依然可以获取到其内部成员的可变引用,进而对其数据进行修改。),如 Mutex<T> ,其中 T 未实现 Sync trait。

#[stable(feature = "rust1", since = "1.0.0")]
unsafe impl<T: ?Sized + Send> Send for Mutex<T> {}
#[stable(feature = "rust1", since = "1.0.0")]
unsafe impl<T: ?Sized + Send> Sync for Mutex<T> {}

因为 Mutex 使用锁来保护对内部数据的访问,如果多个线程同时访问它,可能会导致数据竞争或死锁。

举例来说:

use std::sync::Mutex;

let m = Mutex::new(5); //Mutex<i32> is not Sync

Mutex<i32> 类型没有实现 Sync ,所以跨线程共享是不安全的。

为在多个线程安全地访问非 Sync 类型(如 Mutex<i32> ),我们必须使用适当的同步操作,如获取锁,执行操作和释放锁,在本文后面看到使用互斥锁和其他线程安全类型的示例。

支持 Sync 的类型

Rust 中的 Sync trait 确保了对同一数据的多个引用(无论是可变的还是不可变的)可以安全地从多个线程并发访问。任何实现 Sync trait 的类型 T 都可以被认为是“线程安全”的。

Rust 中的 Sync 类型的一些例子是:

  • 原始类型,如 i32boolchar 等。
  • 简单的聚合类型,如元组 (i32, bool)
  • 原子类型,如 AtomicBool

另一方面,非同步类型不能同时使用多个引用,因为这可能导致数据竞争。非同步类型的一些示例包括:

  • Mutex<i32> - 在访问内部 i32 之前需要锁定互斥体。
  • RefCell<i32> - 在访问内部值之前需要借用 RefCell。
  • Rc<i32> - 共享了内部 i32 的所有权,所以多个可变借用是不安全的。

非 Sync 类型多线程访问

Mutex

为在多个线程安全地访问非同步类型,我们需要使用同步原语,如互斥锁。若仅仅使用 Mutex 而不使用 Arc ,可使用像作用域线程(crossbeam),例如:

这里,我们使用 Mutex<i32> 来安全地从多个线程中修改和读取内部 String。 lock() 方法获取锁,阻止其他线程访问互斥体。

Atomic

AtomicU64 这样的原子类型也可以使用像 fetch_add() 这样的原子操作从多个线程安全地访问。例如:

总结

因此,总而言之,要在 Rust 中跨线程共享数据,数据必须:

  • 类型为 Sync (原始/不可变类型)
  • 封装在互斥或原子类型中(Mutex、RwLock、Atomic*)
  • 使用像通道这样的消息传递技术来跨线程传递数据的所有权。

The Send Trait

Rust 中的 Send trait 表示类型可以安全地跨线程边界传输。如果一个类型实现了 Send ,这意味着该类型的值的所有权可以在线程之间转移。

例如,像 i32bool 这样的原始类型是 Send

因为它们在线程之间共享时没有任何内部引用或可变而导致问题:

然而,像 Rc<i32> 这样的类型未实现 Send ,因为它的引用计数在内部发生了变化,并且多个线程改变相同的引用计数可能会导致内存不安全:

Rc<T> 这样的非 Send 类型不能跨线程传输,但它们仍然可以在单个线程中使用。当线程需要共享一些数据时,非 Send 类型可以被包装在像 Arc<T> 这样的线程安全的包装器中,Arc 使用原子操作来管理引用计数,并允许内部类型在线程之间共享。

总结一下,关于 Send 的几个关键点是:

  • 类型 Send 可以在线程之间转移所有权
  • i32bool 这样的原始类型是 Send
  • 具有内部可变的类型(如 Rc<T> )通常不是 Send
  • Send 类型仍然可以在单个线程中使用,或者在包装在像 Arc<T> 这样的线程安全的容器中时在线程之间共享
  • 跨线程传输非 Send 类型会导致未定义的行为和内存不安全

自定义实现 Sync 和 Send

要创建自定义类型 SyncSend ,您只需实现类型的 SyncSend trait。

这里有一个 持有裸指针*const u8MyBox 结构体, 由于只要复合类型中有一个成员不是 Send 或者 Sync,那么该类型也就不是 Send 或 Sync。裸指针*const u8 均未实现 SendSync TraitMyBox 复合类型也不是 SendSync

若给 MyBox 实现了 Send 和 Sync 则借助 Arc 可在线程间传递和共享数据。当然建议自己不要轻易去实现 Sync 和 Send Trait ,一旦实现就要为被实现类型的线程安全性负责。这件事本来就是一件很难保证的事情。

有些类型是不可能生成SyncSend的,因为它们包含非Sync/非Send类型或允许多线程的可变。例如, Rc<T>不能被设置为Send,因为引用计数需要被原子地更新,而RefCell<T>不能被设置为  Sync,因为它的借用检查不是线程安全的。

同步/发送规则和最佳实践

重要的是要记住混合Sync/Send  和非Sync/非Send类型的规则。一些需要遵守的关键规则:

类型必须是Send才能在线程之间移动。这意味着像Rc<T>这样的类型不能跨线程共享,因为它们不是Send 。

  • 如果一个类型包含一个非Send类型,那么外部类型不能是Send。例如Option<Rc<i32>>不是  Send ,因为Rc<i32>不是Send 。
  • Sync类型可以通过共享引用从多个线程并发使用。非Sync类型不能同时使用它们的值,并且一次只能在一个线程中可变。
  • 如果一个类型包含一个非Sync类型,那么外部类型不能是Sync。例如Mutex<Rc<i32>>不是Sync ,因为Rc<i32>不是Sync 。

并发 Rust 代码的一些最佳实践:

  • 尽可能避免可变。支持不可变的数据结构和逻辑。
  • 当需要修改时,使用同步原语(如Mutex<T>)来安全地从多个线程进行。
  • 使用消息传递在线程之间进行通信,而不是直接共享内存。这有助于避免数据竞争和未定义的行为。
  • 尽可能地限制为修改锁定数据的范围。持有锁太长时间会影响性能和吞吐量。
  • 根据它们是否实现SyncSend仔细选择类型。例如,在线程之间共享时,首选Arc<T>而不是  Rc<T> 。
  • 使用atomic类型进行简单的并发访问原语类型。它们允许从多个线程访问而不加锁。

参考链接

Concurrency in Rust: The Sync and Send Traits | by Technocrat | CoderHack.com | Medium

[基于 Send 和 Sync 的线程安全 - Rust 语言圣经(Rust Course)](https://course.rs/advance/concurrency-with-threads/send-sync.html "基于 Send 和 Sync 的线程安全 - Rust 语言圣经(Rust Course "基于 Send 和 Sync 的线程安全 - Rust 语言圣经(Rust Course)")")

Rust 中的 Arc 和 Mutex|关键在于 --- Arc and Mutex in Rust | It's all about the bit

Rust 入门与实践

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

排序算法大总结

引言 排序算法&#xff08;sorting algorithm&#xff09;是用于对一组数据按照特定顺序进行排列。排序算法有着广泛的应用&#xff0c;因为有序数据通常能够被更高效地查找、分析和处理。 如图 1-1 所示&#xff0c;排序算法中的数据类型可以是整数、浮点数、字符或字符串等…

SpringBoot中实现发送邮件

概要 在Spring Boot中发送电子邮件相对简单。你可以使用Spring的邮件支持来实现这一点。 步骤&#xff1a; 1.添加依赖&#xff1a;首先&#xff0c;需要在你的pom.xml文件中添加Spring Boot的邮件发送器依赖。 2. 配置邮件服务器&#xff1a;在application.properties或app…

Python 机器学习 基础 之 学习 基础环境搭建

Python 机器学习 基础 之 学习 基础环境搭建 目录 Python 机器学习 基础 之 学习 基础环境搭建 一、简单介绍 二、什么是机器学习 三、python 环境的搭建 1、Python 安装包下载 2、这里以 下载 Python 3.10.9 为例 3、安装 Python 3.10.9 4、检验 python 是否安装成功&…

设计模式第二次测试 | 数据库连接池设计(原型模式、创建者模式、适配器模式)

需求中文如下&#xff1a;原本是英文&#xff0c;用百度翻译转换而来 我们需要设计一个工具&#xff0c;它负责创建一个与数据库软件MySQL的连接池。 连接池中有数百个连接可供客户端使用。 所有连接对象都有相同的内容&#xff0c;但它们是不同的对象。 连接对象的创建是资源密…

【嵌入式笔试题】进程线程笔试题

非常经典的笔试题。 1.进程&线程(16道) 1.1异步IO和同步IO区别? 答案:如果是同步IO,当一个IO操作执行时,应用程序必须等待,直到此IO执行完。 相反,异步IO操作在后台运行,IO操作和应用程序可以同时运行,提高系统性能,提 高IO流量。 解读:在同步文件IO中,线…

Sarcasm detection论文解析 |用于微博讽刺检测的上下文增强卷积神经网络

论文地址 论文地址&#xff1a;Context-augmented convolutional neural networks for twitter sarcasm detection - ScienceDirect 论文首页 笔记大纲 用于微博讽刺检测的上下文增强卷积神经网络 &#x1f4c5;出版年份:2018 &#x1f4d6;出版期刊:Neurocomputing &#x1f…

Django后台项目开发实战五

完成两个功能&#xff1a; HR 可以维护候选人信息面试官可以录入面试反馈 第五阶段 创建 interview 应用&#xff0c;实现候选人面试评估表的增删改功能&#xff0c;并且按照页面分组来展示不同的内容&#xff0c;如候选人基础信息&#xff0c;一面&#xff0c;二面的面试结…

在离线环境中将 CentOS 7.5 原地升级并迁移至 RHEL 7.9

《OpenShift / RHEL / DevSecOps 汇总目录》 说明 本文将说明如何在离线环境中将 CentOS 7.5 升级并迁移至 RHEL 7.9。为了简化准备过程&#xff0c;本文前面将在在线环境中安装用到的各种所需验证软件&#xff0c;而在后面升级迁移的时候再切换到由 ISO 构成的离线 Yum Repo…

20240430,类模板案例-数组类封装,STL初识,STRING容器(构造函数,赋值)

我真的碎掉了&#xff0c;主要是我很缺那点钱啊现在&#xff0c;我真的碎掉了我碎掉了碎掉了碎掉了 0.8 类模板案例-数组类封装 需求&#xff1a;1&#xff0c;存储&#xff1a;内置和自定义数据类型&#xff1b; 2&#xff0c;存到堆区&#xff1b; 3&#xff0c;构造函数传入…

Docker部署RabbitMQ与简单使用

官网地址&#xff1a; Messaging that just works — RabbitMQ 我的Docker博客:Docker-CSDN博客 1.结构 其中包含几个概念&#xff1a; **publisher**&#xff1a;生产者&#xff0c;也就是发送消息的一方 **consumer**&#xff1a;消费者&#xff0c;也就是消费消息的一方 …

第三节课,功能2:开发后端用户的管理接口--http client -- debug测试

一、idea 中 Http client 使用 二、测试步骤&#xff0c;先进入主程序 2.1 先run &#xff0c;再debug 2.2 再进入想要测试的代码 2.2.1 进入测试的接口 三、程序逻辑 1&#xff09;用户注册逻辑&#xff1a;如果用户不存在再后端&#xff0c;看用户名&密码&校验码是…

一些优雅的监控运维技巧

准备工作 安装 sysstat sudo apt install sysstat查看某个进程的cpu情况 pidstst -u -p 256432查看某个进程的RAM情况 pidstst -r -p 256432查看某个进程的IO情况 pidstst -d -p 256432查看某个进程下的线程执行情况 pidstst -t -p 256432查看指定PID的进程对应的可执行文件…

ip ssl证书无限端口网站

IP SSL证书是由CA认证机构颁发的一种特殊数字证书。大部分SSL数字证书都需要用户使用域名进行申请&#xff0c;想要对公网IP地址加密实现https访问就需要申请IP SSL证书。IP SSL证书采用了强大的加密算法&#xff0c;可以有效地防止数据在传输过程中被窃取或篡改&#xff0c;具…

nbcio-boot基于jeecg的flowable支持部门经理的单个或多实例支持(前端部分)

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 演示地址&#xff1a;RuoYi-Nbcio后台管理系统 http://218.75.87.38:9666/ 更多nbcio-boot功能请看演示系统 gitee源代码地址 后端代码&#xff1a; h…

特斯拉与百度合作;字节正全力追赶AI业务;小红书内测自研大模型

特斯拉中国版 FSD 或与百度合作 根据彭博社的报道&#xff0c;特斯拉将通过于百度公司达成地图和导航协议&#xff0c;扫清在中国推出 FSD 功能的关键障碍。 此前&#xff0c;中国汽车工业协会、国家计算机网络应急技术处理协调中心发布《关于汽车数据处理 4 项安全要求检测情…

大数据组件之Storm详解

Storm 是一个免费并开源的分布式实时计算系统&#xff0c;具有高容错性和可扩展性。它能够处理无边界的数据流&#xff0c;并提供了实时计算的功能。与传统的批处理系统相比&#xff0c;Apache Storm 更适合处理实时数据。 让我们深入了解一下 Storm&#xff1a; 1.Storm 简介…

Systemback Ubuntu14.04 制作自定义系统ISO镜像

工作需要&#xff0c;要基于ubuntu自定义一些编译环境并将自己配置好的ubuntu做成镜像。 硬件准备 ​ 为保证能够顺利完成系统iso镜像的制作与系统还原&#xff0c;推荐准备一个较大容量的U盘或者移动固态硬盘&#xff0c;同时确保自己的Ubuntu系统还有比较大的可用空间。 1 S…

sgg_ssm学习--前端搭建遇到的问题

目录 问题1&#xff1a;由于我是解压缩软件nodejs&#xff0c;没有添加系统路径 解决&#xff1a;添加nodejs的路径 到系统 path中 问题2&#xff1a;vscode 终端输入npm命令 报错 解决(如图所示在vscode打开前端工程&#xff0c;终端修改如下配置)&#xff1a; 问题1&…

CSS 伪类、伪元素的应用实例:电池充电、高能进度条

一、目的 本文通过 CSS 伪类、伪元素&#xff0c;结合动画 animation 和 Vue 动态样式属性&#xff08;通过 CSS 变量&#xff09;的写法&#xff0c;来实现电池充电、高能进度条的效果&#xff0c;如下图所示。 二、基础知识 1、CSS 伪类、伪元素 简单概括成以下 4 点&#x…

如何提升制造设备文件汇集的可靠性和安全性?

制造设备文件汇集通常指的是将与制造设备相关的各种文档和资料进行整理和归档的过程。这些文件可能包括但不限于&#xff1a; 生产数据&#xff1a;包括生产计划、订单信息、生产进度等。 设计文件&#xff1a;如CAD图纸、设计蓝图、产品模型等。 工艺参数&#xff1a;用于指…