金属Rust:原子操作

金属Rust:原子操作

对于复杂的操作来说,使用互斥量(Mutex)来避免竞态条件相当省力。但是对于一些比较小规模的操作,比如让一个计数器+1之类,会考虑更方便的原子对象。

原子类型在标准库中的std::sync::atomic模块下。原子类型和平时使用的基础类型(primitive type)很像,唯一的区别是原子类型的操作能够保证对数据操作的访问顺序。也就是说,如果当前线程对一个变量进行的修改如果没有完成,其他线程是无法访问到该变量的。

操作顺序

对于一些比较耗时的操作,CPU会采用乱序执行的策略来保证执行效率。但是这也带来了一个问题:代码的执行顺序被改变了,如果这个变量。而这个顺序变化是由CPU产生的,关编译器优化也救不了你。那么如果不同的线程在同时访问两个不同的原子变量,执行逻辑可能就会发生改变:

比如说你觉得它是这么执行的:

  1. 线程A向flag1写入true
  2. 线程B读取flag1,发现值为true,继续执行;
  3. 线程A向flag2写入true
  4. 线程B读取flag2,发现值为true,继续执行;
  5. 执行业务逻辑。

然而实际上,它是这么执行的:

  1. 线程A向flag2写入true
  2. 线程B读取flag1,发现值为false,放弃执行;
  3. 线程A向flag1写入true

整个逻辑树都错误了!于是为了处理这种情况,我们会使用std::sync::atomic::Ordering来确定执行顺序。保证期望操作会按期望种的顺序执行。

Rust的执行顺序基本和LLVM的对应:

顺序 说明
Relaxed 弃疗,只进行原子操作不管执行顺序。简单的计数器可以考虑使用这个。
Acquire 如果是读取,保证在这次原子操作后的代码在其之后执行,之前的操作可能会被置后。阻止处理器执行后续指令。
Release 如果是写入,保证在这次原子操作前的代码在其之前执行,后续的操作可能会被提前。让处理器执行完所有前面的指令。
AcqRel 在读取的时候等同于Acquire;在写入的时候等同于Release。但这并不代表AcqRel会根据load()/store()自动适配,实际上load()/store()的时候使用AcqRel会导致线程panic。
SeqCst 保证指令位置和处理器执行位置 完 全 一 致。保证完全的顺序正确,是最安全的顺序,但是可能会降低执行速度。

举个例子

但是这个执行顺序到底是用来干嘛的?原子操作跟操作重排有什么关系?

我们拿std::sync::atomic::AtomicBool来举个例子。比如说有个原子布尔量叫做flag,我们通过这个原子量来实现一个自旋锁。flag的值为true的时候表示线程得锁。

while flag.compare_and_swap(false, true, Ordering::Acquire) {
    yield_now()
}
// 访问临界数据..
flag.store(false, Ordering::Release);

使用Acquire顺序保证了,在夺得锁之前,所有应该在夺锁后发生的操作不会被排到前面去。比如说后面要操作一个RefCell,莫名其妙在拿到锁之前就进行borrow_mut(),而这个时候如果夺到锁的线程还在可变借用这个RefCell,线程就会因为访问问题panic了。

而后面的Release顺序保证了,在释放锁的时候,所有应该在释放前进行的操作都已经完成。也可以参考刚才Acquire的例子,在释放锁之后才调用了borrow_mut()什么的。

你可能已经发现了,AcquireRelease顺序的名称其实是指对原子锁的操作:“acquire a lock”和“release a lock”。不过毕竟我阅历浅薄,对于原子操作有严格顺序要求的需求暂时还只见过这个例子,无法进行更多的叙述了。

AcqRel的情况比较特殊,可能后面会另外开篇文章说一下。

原文地址:https://www.cnblogs.com/penguinliong/p/12591475.html

时间: 2024-10-05 04:56:04

金属Rust:原子操作的相关文章

金属Rust:`Borrow`与`AsRef`

金属Rust:Borrow与AsRef 最近在调整Writium的结构,自己果然开始嫌弃Iron麻烦了.造轮子的灵魂觉醒了!那么在包裹Hyper的Request和Response的过程中,遇到了个问题:到底应该用什么取引用好呢? 在std中我们能找到两个用于取引用的trait:Borrow和AsRef(以及对应的BorrowMut和AsMut).还是挺容易混淆的,毕竟好像它们的意思都是取引用,"借"走一个对象.那么就先来看看文档是怎么说的. Borrow Borrow在文档中的定义如下

Rust的“并发安全”设计

部分内容参考Aaron Turon的文章<Fearless Concurrency with Rust > 昨天发了一篇说异步IO和轻量级线程的文章,有人问我为什么不在后面补充一下rust的并发模型.其实rust并不存在一个独特的并发模型,但它从语言层面上提供了一整套机制来保证并发的安全,借助这套机制,你可以安全的实现很多并发模型,如消息传递式.共享状态式.无锁式和纯函数式.昨天晚上我在群里和人讨论有关设计的话题,我觉得一个好的设计应当有两方面表现,一方面是符合直觉,也就是各个方面保证一致性以

QCon2016演讲《Rust语言的核心竞争力》总结和补充

应 QCon北京2016|全球软件开发大会 主编臧秀涛邀请,我(Liigo)于2016年4月23日在大会上做主题演讲<Rust编程语言的核心优势和核心竞争力>(PDF演讲稿).由于是初次登台,现场表现不佳,个人不是很满意.故做本文对此次演讲进行总结和补充. 核心三要素:系统编程,零运行时,内存安全 我把Rust编程语言的核心优势和核心竞争力概括为三个要素:系统编程,零运行时,内存安全.在强调底层控制的系统编程领域,同时保持极小的运行时开销和极高的运行时效率,又保证了系统内存安全的现代编程语言,

为什么我说Rust是靠谱的编程语言

为什么我说Rust是靠谱的编程语言 作者:Liigo(庄晓立) 时间:2015年5月16日 原创链接:http://blog.csdn.net/liigo/article/details/45757123 版权声明:未经作者许可不得转载:授权转载需注明出处. 序言:本文试图帮您解答"我要不要(投入大量时间和精力)学习Rust语言?"这个问题.作者尽量较少的谈及Rust语言本身,反而尝试从Rust语言周边入手,长时间.大范围.多角度地考察,研判Rust语言是否靠谱,并给出尽可能客观的理由

Rust入坑指南:齐头并进(下)

前文中我们聊了Rust如何管理线程以及如何利用Rust中的锁进行编程.今天我们继续学习并发编程, 原子类型 许多编程语言都会提供原子类型,Rust也不例外,在前文中我们聊了Rust中锁的使用,有了锁,就要小心死锁的问题,Rust虽然声称是安全并发,但是仍然无法帮助我们解决死锁的问题.原子类型就是编程语言为我们提供的无锁并发编程的最佳手段.熟悉Java的同学应该知道,Java的编译器并不能保证代码的执行顺序,编译器会对我们的代码的执行顺序进行优化,这一操作成为指令重排.而Rust的多线程内存模型不

第3章 文件I/O(3)_内核数据结构、原子操作

3. 文件I/O的内核数据结构 (1) 内核数据结构表 数据结构 主要成员 文件描述符表 ①文件描述符标志 ②文件表项指针 文件表项 ①文件状态标志(读.写.追加.同步和非阻塞等状态标志) ②当前文件偏移量 ③i节点表项指针 ④引用计数器 i节点 ①文件类型和对该文件的操作函数指针 ②当前文件长度 ③文件所有者 ④文件所在设备.文件访问权限 ⑤指向文件数据在磁盘块上所在位置的指针等. (2)3张表的关系 4. 文件的原子操作 (1)文件追加 ①打开文件时使用O_APPEND标志,进程对文件偏移量

C++拾遗--多线程:原子操作解决线程冲突

C++拾遗--多线程:原子操作解决线程冲突 前言 在多线程中操作全局变量一般都会引起线程冲突,为了解决线程冲突,引入原子操作. 正文 1.线程冲突 #include <stdio.h> #include <stdlib.h> #include <process.h> #include <Windows.h> int g_count = 0; void count(void *p) { Sleep(100); //do some work //每个线程把g_c

MIPS平台OpenWrt路由器系统内的Rust应用程序开发

作者:Liigo(庄晓立) 日期:2014年9月17日 原创链接:http://blog.csdn.net/liigo/article/details/39347541 版权所有,转载请注明出处:http://blog.csdn.net/liigo 目标 使用 Rust 语言,交叉编译开发 MIPS(el) + OpenWrt 路由器平台下的应用软件. 编译rustc 首先自行编译Rust编译器源代码,生成支持 mipsel-linux 平台的交叉编译器rustc ./configure --t

Rust 1.7.0 匹配器 match 的简介和使用

使用过正則表達式的人应该都知道 matcher ,通过 matcher 匹配器运算正則表達式,完毕一系列的匹配规则. 在Rust 中 没有 switch 语句.matcher 就是 switch 的一个变形,但比其它语言中的 switch 更强大! 一.简单举例说明 简单的 matcher 和 if 语句很相似,假设是简单的条件推断能够用if语句: let n = 5; if n < 0 { print!("{} is negative", n); } else if n >