go atomic原子操作详细解读

文章目录

  • 概要
  • 1、基本知识
    • 1.1 原子操作是什么
    • 1.2 CPU怎么实现原子操作的?
  • 2、atomic包
    • 2.1、 Add函数
    • 2.2、CompareAndSwap函数
    • 2.3、Swap函数
    • 2.4、Load函数
    • 2.5、Store函数
  • 3、atomic.Value值

概要

atomic包是golang通过对底层系统支持的原子操作进行封装,而提供的原子操作包,用于实现无锁化并发安全的操作数据。支持加法(Add)、比较并交换(CompareAndSwap)、直接交换(Swap)、设置指针变量值(Store)、获取指针变量值(Load);还设置了atomic.Value结构,支持对象存储、获取、比较并交换、直接交换操作。

atmoic包怎么用?以加法为例,变量加法运算操作并不是原子性的,包括从内存中取值放入加法寄存器、寄存器运算得到结果、将结果写回内存,在并发环境下,一个协程的运算结果可能被另一个覆盖,从而导致丢失修改的问题,例如以下代码的最后打印结果不会是10000。

var wg sync.WaitGroup
var num = 0func Add() {num++wg.Done()
}
func main() {size := 10000wg.Add(size)for i := 0; i < size; i++ {go Add()}wg.Wait()fmt.Println(num)
}

而以下代码时10000:

var wg sync.WaitGroup
var num int32 = 0func Add() {atomic.AddInt32(&num, 1)wg.Done()
}
func main() {size := 10000wg.Add(size)for i := 0; i < size; i++ {go Add()}wg.Wait()fmt.Println(num)
}

1、基本知识

1.1 原子操作是什么

原子操作是指在执行过程中不会被中断的操作,要么全部执行完成,要么完全不执行,不存在部分执行的情况。原子操作可以看作是一个不可分割的单元,其他线程或进程无法在其中间进行干扰或插入。
原子操作通常用于对共享数据进行读取、写入和修改等操作,以保证数据的一致性和正确性。在多线程或多进程环境下,如果多个线程或进程同时访问和修改同一份共享数据,没有正确的同步机制或使用原子操作可能会导致数据竞争和不确定的行为。
常见的原子操作包括:

  • 原子读(Atomic Read):从内存中获取一个共享变量的值,并确保其他线程或进程不会在读取过程中修改该值。
  • 原子写(Atomic Write):将一个值写入到共享变量,并确保其他线程或进程不会在写入过程中读取或修改该值。
  • 原子加(Atomic Add):将一个特定的值与共享变量相加,并将结果存回共享变量中,确保其他线程或进程不会在加法过程中干扰。
  • 原子比较并交换(Atomic Compare-and-Swap):比较共享变量的当前值与预期值是否相等,如果相等,则将新值写入共享变量;如果不相等,则不做任何修改。该操作常用于实现同步机制和锁。

1.2 CPU怎么实现原子操作的?

现在的CPU一般为多核处理器,它底层实现原子操作的方式有多种,下面介绍几种常见的方法:

  1. 总线锁定:在多核处理器中,可以使用总线锁定机制来确保原子操作。当一个处理器需要执行原子操作时,它会向总线发送一个请求锁定的信号。其他处理器在接收到该信号后,将暂停对总线的访问,以防止干扰正在进行的原子操作。只有当原子操作完成并释放锁定时,其他处理器才能继续对总线进行访问。

  2. 缓存一致性协议:多核处理器中的每个核心通常都具有自己的缓存,这就需要确保各个核心之间的缓存数据的一致性。缓存一致性协议可以通过在多个核心之间共享和更新处理器**缓存行(缓存在cpu核心本地缓存中的一条数据)**的状态信息来实现原子操作。常见的缓存一致性协议有MESI,多个核心更新数据时需要遵循以下原则:

    • Modified(修改)状态:当一个核心修改了一个缓存行中的数据时,该缓存行将被标记为“修改”状态。此时,其他核心的缓存中对应的缓存行就被视为无效(Invalid),即无效数据。

    • Exclusive(独占)状态:当一个核心从内存中加载了一个缓存行到自己的缓存中,并且该缓存行在其他核心的缓存中没有副本时,该缓存行处于“独占”状态。这时,其他核心需要读取或写入该数据时,必须通过该核心进行访问,独占状态的数据,可以直接进行修改。

    • Shared(共享)状态:当多个核心都拥有同一个缓存行的副本时,该缓存行处于“共享”状态。此时,对于读操作,其他核心可以直接从自己的缓存中读取数据。对于写操作,需要通过总线发出命令让其他缓存的状态更新为“失效”,该缓存中数据更新为独占,才能修改。

    • Invalid(无效)状态:当一个核心修改了一个缓存行的数据时,该缓存行在其他核心的数据状态会被标记为“无效”状态。这意味着其他核心需要重新从内存中加载最新的数据。

      总的逻辑是,当一个核心读取内存中数据在自己的本地缓存中并且只有该核心的缓存拥有该缓存行,这个缓存行的状态为独占状态,此时可直接进行读写操作,其它缓存读取该缓存行需要通过总线想该核心发送请求进行读取,此时缓存行状态变为共享。核心可以读取缓存行数据,但修改需要先通过总线发起修改请求,将其它核心的该缓存行数据置为无效,再修改缓存行中数据(看起来效率很低?其实cpu会马上修改该缓存行,修改完后缓存不会马上生效,而是放入store buffer中,再反送失效请求给所有核心,由store buffer等待全部核心响应成功,再生效缓存行的修改,这段期间核心可以继续做其它事情)。修改完成后,缓存行为修改状态,其他核心的该缓存行为无效状态,此时若有核心读取该缓存行,就将缓存行写入内存,该核心上的缓存行状态更新为共享;若是修改请求,就将核心缓存行状态更新为无效。

  3. 原子指令:现代的多核处理器通常会提供一些原子指令,例如比较并交换(compare and swap)、加载和存储条件(load-linked/store-conditional)等。这些指令能够以硬件级别的原子方式执行,从而保证对共享数据的访问是原子的。通过使用这些原子指令,可以避免锁的开销,并提供更高效的原子操作。

2、atomic包

Golang的atomic包的原子操作是通过CPU指令实现的。在大多数CPU架构中,原子操作的实现都是基于32位或64位的寄存器。Golang的atomic包的原子操作函数会将变量的地址转换为指针型的变量,并使用CPU指令对这个指针型的变量进行操作。
Golang的atomic包提供了一组原子操作函数,包括Add、CompareAndSwap、Load、Store、Swap等函数。这些函数的具体作用如下:

  • Add函数:用于对一个整数型的变量进行加法操作,并返回新的值。
  • CompareAndSwap函数:用于比较并交换一个指针型的变量的值。如果变量的值等于旧值,就将变量的值设置为新值,并返回true;否则,不修改变量的值,并返回false。
  • Load函数:用于获取一个指针型的变量的值。
  • Store函数:用于设置一个指针型的变量的值。
  • Swap函数:用于交换一个指针型的变量的值,并返回旧值。
    让我们更具体地来看一下Golang的atomic包的原子操作:

2.1、 Add函数

Add函数用于对一个整数型的变量进行加法操作,并返回新的值。Add函数的定义如下:

func AddInt32(addr *int32, delta int32) (new int32)
func AddInt64(addr *int64, delta int64) (new int64)
func AddUint32(addr *uint32, delta uint32) (new uint32)
func AddUint64(addr *uint64, delta uint64) (new uint64)
func AddUintptr(addr *uintptr, delta uintptr) (new uintptr)

其中,addr表示要进行加法操作的变量的地址,delta表示要加上的值。Add函数会将变量的值加上delta,并返回新的值。

2.2、CompareAndSwap函数

CompareAndSwap函数用于比较并交换一个指针型的变量的值。如果变量的值等于旧值,就将变量的值设置为新值,并返回true;否则,不修改变量的值,并返回false。CompareAndSwap函数的定义如下:

func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool)
func CompareAndSwapInt64(addr *int64, old, new int64) (swapped bool)
func CompareAndSwapUint32(addr *uint32, old, new uint32) (swapped bool)
func CompareAndSwapUint64(addr *uint64, old, new uint64) (swapped bool)
func CompareAndSwapUintptr(addr *uintptr, old, new uintptr) (swapped bool)
func CompareAndSwapPointer(addr *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool)

其中,addr表示要进行比较和交换的变量的地址,old表示旧值,new表示新值。如果变量的值等于旧值,就将变量的值设置为新值,并返回true;否则,不修改变量的值,并返回false。

2.3、Swap函数

Swap函数用于交换一个指针型的变量的值,并返回旧值。Swap函数的定义如下:

func SwapInt32(addr *int32, new int32) (old int32)
func SwapInt64(addr *int64, new int64) (old int64)
func SwapUint32(addr *uint32, new uint32) (old uint32)
func SwapUint64(addr *uint64, new uint64) (old uint64)
func SwapUintptr(addr *uintptr, new uintptr) (old uintptr)
func SwapPointer(addr *unsafe.Pointer, new unsafe.Pointer) (old unsafe.Pointer)

其中,addr表示要交换的变量的地址,new表示新值。Swap函数会将变量的值设置为new,并返回旧值。

2.4、Load函数

Load函数用于获取一个指针型的变量的值。Load函数的定义如下

func LoadInt32(addr *int32) (val int32)
func LoadInt64(addr *int64) (val int64)
func LoadUint32(addr *uint32) (val uint32)
func LoadUint64(addr *uint64) (val uint64)
func LoadUintptr(addr *uintptr) (val uintptr)
func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer)

2.5、Store函数

Store函数用于设置一个指针型的变量的值。Store函数的定义如下:

func StoreInt32(addr *int32, val int32)
func StoreInt64(addr *int64, val int64)
func StoreUint32(addr *uint32, val uint32)
func StoreUint64(addr *uint64, val uint64)
func StoreUintptr(addr *uintptr, val uintptr)
func StorePointer(addr *unsafe.Pointer, val unsafe.Pointer)

其中,addr表示要设置的变量的地址,val表示要设置的值。Store函数会将变量的值设置为val。

3、atomic.Value值

可以用来存储any类型对象的结构体,存取操作均是原子操作,具体方法如下:

type Value struct {v any
}
// 加载Value中存的值
func (v *Value) Load() (val any) {}
// 存储对象放入Value中
func (v *Value) Store(val any) {}
// 交换Value中存的数据
func (v *Value) Swap(new any) (old any) {)
// 比较并存储(值必须铜类型且可比较采用使用该方法,否则panic);传入old、new对象,如果old对象等于Value内存储的对象,就将新的对象存入,返回treu。否则false
func (v *Value) CompareAndSwap(old, new any) (swapped bool) {}

实践一下:

package mainimport ("fmt""sync/atomic"
)type Config struct {Addr string
}var config atomic.Valuefunc main() {conf1 := Config{Addr: "1.1.1.1",}conf2 := Config{Addr: "2.2.2.2",}config.Store(conf1)oldData := config.Swap(conf2)newData := config.Load()fmt.Println(oldData, newData)conf3 := Config{Addr: "3.3.3.3",}ok := config.CompareAndSwap(conf1, conf3)fmt.Println(ok, config.Load())ok = config.CompareAndSwap(conf2, conf3)fmt.Println(ok, config.Load())
}

输出:

{1.1.1.1} {2.2.2.2}
false {2.2.2.2}
true {3.3.3.3}

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

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

相关文章

华为将收取蜂窝物联网专利费,或将影响LPWAN市场发展

近日&#xff0c;华为正式公布了其4G和5G手机、Wi-Fi6设备和物联网产品的专利许可费率&#xff0c;其中包含了长距离通信技术蜂窝物联网。作为蜂窝物联网技术的先驱&#xff0c;华为是LTE Category NB (NB-IoT)、LTE Category M和其他4G物联网标准的主要贡献者。 在NB-IoT领域…

基于traccar快捷搭建gps轨迹应用

0. 环境 - win10 虚拟机ubuntu18 - i5 ubuntu22笔记本 - USB-GPS模块一台&#xff0c;比如华大北斗TAU1312-232板 - 双笔记本组网设备&#xff1a;路由器&#xff0c;使得win10笔记本ip&#xff1a;192.168.123.x&#xff0c;而i5笔记本IP是192.168.123.215。 - 安卓 手机 1.…

React2023电商项目实战 - 1.项目搭建

古人学问无遗力&#xff0c;少壮工夫老始成。 纸上得来终觉浅&#xff0c;绝知此事要躬行。 —— 陆游《《冬夜读书示子聿》》 系列文章目录 项目搭建App登录及网关App文章自媒体平台&#xff08;博主后台&#xff09;内容审核(自动) 文章目录 系列文章目录一、项目介绍1.页面…

ubuntu安装Microsoft Edge并设置为中文

1、下载 edge.deb 版本并安装 sudo dpkg -i microsoft-edg.deb 2. 设置默认中文显示 如果是通过.deb方式安装的&#xff1a; 打开默认安装路径下的microsoft-edge-dev文件&#xff0c;在文件最开头加上: export LANGUAGEZH-CN.UTF-8 &#xff0c;保存退出。 cd /opt/micr…

python绘制谷歌地图

谷歌地图 更多好看的图片见pyecharts官网 import pyecharts.options as opts from pyecharts.charts import MapGlobe from pyecharts.faker import POPULATIONdata [x for _, x in POPULATION[1:]] low, high min(data), max(data)c (MapGlobe().add_schema().add(mapty…

解决el-select回显异常 显示option选项的value 而不是显示label

1、问题 回显的value和选项value类型不同 form中v-model"form.userId"是字符串类型 option中:value“item.userId” 选项id是数字类型 2、办法 :value“item.userId” 改为 :value“item.iduserId‘’”&#xff08;转换成字符串&#xff09; <el-form-item l…

面试之快速学习STL-map

关联式容器 包括 map、multimap、set 以及 multiset 这 4 种容器。和序列式容器区别&#xff1a; a . 关联式容器在存储元素时还会为每个元素在配备一个键&#xff0c;整体以键值对的方式存储到容器中。 b . 相比前者&#xff0c;关联式容器可以通过键值直接找到对应的元素&am…

【100天精通python】Day42:python网络爬虫开发_HTTP请求库requests 常用语法与实战

目录 1 HTTP协议 2 HTTP与HTTPS 3 HTTP请求过程 3.1 HTTP请求过程 3.2 GET请求与POST请求 3.3 常用请求报头 3.4 HTTP响应 4 HTTP请求库requests 常用语法 4.1 发送GET请求 4.2 发送POST请求 4.3 请求参数和头部 4.4 编码格式 4.5 requests高级操作-文件上传 4.6 …

Spring Boot 统一功能处理

目录 1.用户登录权限效验 1.1 Spring AOP 用户统一登录验证的问题 1.2 Spring 拦截器 1.2.1 自定义拦截器 1.2.2 将自定义拦截器加入到系统配置 1.3 拦截器实现原理 1.3.1 实现原理源码分析 2. 统一异常处理 2.1 创建一个异常处理类 2.2 创建异常检测的类和处理业务方法 3. 统一…

go-test

单元测试 基本用法 Go语言测试 常用reflect.DeepEqual()对slice进行比较 跳过某些测试用例 func TestTimeConsuming(t *testing.T) {if testing.Short() {t.Skip("short模式下会跳过该测试用例")}... }当执行go test -short时就不会执行上面的TestTimeConsuming测…

python http文件上传

server端代码 import os import cgi from http.server import SimpleHTTPRequestHandler, HTTPServer# 服务器地址和端口 host = 0.0.0.0 port = 8080# 处理文件上传的请求 class FileUploadHandler(SimpleHTTPRequestHandler):def do_POST(self):# 解析多部分表单数据form = …

【Spring系列篇--关于IOC的详解】

目录 面试经典题目&#xff1a; 1. 什么是spring&#xff1f;你对Spring的理解&#xff1f;简单来说&#xff0c;Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。 2.什么是IoC&#xff1f;你对IoC的理解&#xff1f;IoC的重要性?将实例化对象的权利从程序员…

Centos 8 网卡connect: Network is unreachable错误解决办法

现象1、ifconfig没有ens160配置 [testlocalhost ~]$ ifconfig lo: flags73<UP,LOOPBACK,RUNNING> mtu 65536 inet 127.0.0.1 netmask 255.0.0.0 inet6 ::1 prefixlen 128 scopeid 0x10<host> loop txqueuelen 1000 (Local Loopba…

基于深度学习的指针式仪表倾斜校正方法——论文解读

中文论文题目:基于深度学习的指针式仪表倾斜校正方法 英文论文题目&#xff1a;Tilt Correction Method of Pointer Meter Based on Deep Learning 周登科、杨颖、朱杰、王库.基于深度学习的指针式仪表倾斜校正方法[J].计算机辅助设计与图形学学报, 2020, 32(12):9.DOI:10.3724…

CentOS Linux 78安全基线检查

阿里云标准-CentOS Linux 7/8安全基线检查 检查项类别描述加固建议等级密码复杂度检查身份鉴别检查密码长度和密码是否使用多种字符类型编辑/etc/security/pwquality.conf&#xff0c;把minlen(密码最小长度)设置为8-32位&#xff0c;把minclass(至少包含小写字母、大写字母、数…

代码随想录训练营day25| 216.组合总和III 17.电话号码的字母组合

TOC 前言 代码随想录算法训练营day25 一、Leetcode 216.组合总和III 1.题目 找出所有相加之和为 n 的 k 个数的组合&#xff0c;且满足下列条件&#xff1a; 只使用数字1到9 每个数字 最多使用一次 返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次&#xff0c;组…

【Java】智慧工地SaaS平台源码:AI/云计算/物联网/智慧监管

智慧工地是指运用信息化手段&#xff0c;围绕施工过程管理&#xff0c;建立互联协同、智能生产、科学管理的施工项目信息化生态圈&#xff0c;并将此数据在虚拟现实环境下与物联网采集到的工程信息进行数据挖掘分析&#xff0c;提供过程趋势预测及专家预案&#xff0c;实现工程…

《强化学习:原理与Python实战》——可曾听闻RLHF

前言&#xff1a; RLHF&#xff08;Reinforcement Learning with Human Feedback&#xff0c;人类反馈强化学习&#xff09;是一种基于强化学习的算法&#xff0c;通过结合人类专家的知识和经验来优化智能体的学习效果。它不仅考虑智能体的行为奖励&#xff0c;还融合了人类专家…

kafka安装说明以及在项目中使用

一、window 安装 1.1、下载安装包 下载kafka 地址&#xff0c;其中官方版内置zk&#xff0c; kafka_2.12-3.4.0.tgz其中这个名称的意思是 kafka3.4.0 版本 &#xff0c;所用语言 scala 版本为 2.12 1.2、安装配置 1、解压刚刚下载的配置文件&#xff0c;解压后如下&#x…

【机器学习】处理不平衡的数据集

一、介绍 假设您在一家给定的公司工作&#xff0c;并要求您创建一个模型&#xff0c;该模型根据您可以使用的各种测量来预测产品是否有缺陷。您决定使用自己喜欢的分类器&#xff0c;根据数据对其进行训练&#xff0c;瞧&#xff1a;您将获得96.2%的准确率&#xff01; …