一文搞明白golang底层原子级内存操作 的使用(sync atomic包)

2024-06-04 03:44

本文主要是介绍一文搞明白golang底层原子级内存操作 的使用(sync atomic包),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

        在我们的程序开发中,对于并发的处理一直都是一件很头疼的事情(Rust这种天生无并发困扰的语言除外), 在go语言中,官方也给我们提供了底层的原子级内存操作,这对于同步算法的实现是非常有用的。

atomic包使用结论

        由于这个包里面定义的一堆函数官方都不推荐使用,所以这个包里面的函数仅作为参考。我们主要搞明白类型定义和使用即可。 这个atomic包里面的类型定义看似一大推,其实归纳起来就8种,分别是 :Bool, Int32/64, Uint32/64, Pointer, Uintptr, Value ;  还有他们的方法,基本都一样,即 最多也就以下这5个方法:

  1. Add对原子数增加的一个增量数据;
  2. Store将数据存储到原子中,当这个Store被调用的时候,他的数据是不允许被拷贝的;
  3. Load从原子中加载对应的数据;
  4. Swap使用新的数据替换就的数据;
  5. CompareAndSwap比较和交换数据。

只要你搞明白了上面的5个方法的使用,这个atomic包的使用也就明白了。

原子级内存操作使用示例

以下我们就以 atomic.Int64 类型的使用为例,来说明如何使用, 其他的类型使用都是一样的,不同的类型方法多少而已,只要有的方法实用都是一样的。

直接上测试用例, 看明白这个测试用例也就明白了, 里面都有详细的注释说明。

import ("sync/atomic""testing"
)// sync atomic数据类型操作使用示例, 单元测试用例
// 其他的另外7种atomic类型的使用和这个都类似,不在赘述!
// @author tekintian <tekintian@gmail.com>
// @see https://pkg.go.dev/sync/atomic
func TestAtomicType(t *testing.T) {// int64类型的原子数,//  他对于的类型声明是 type Int64 struct {// contains filtered or unexported fields}// 这个地方我们只需要定义就可以,不需要初始化. 其他几种类型也是一样var an1 atomic.Int64// 定义了变量后,我们就可以操作他对应的5个方法了an1.Store(10)     // 把 10存入这个原子变量an1.Add(1)        // 增量加1 类似于  10+1ret := an1.Load() // Load取出结果,这里ret为 11if ret != 11 {t.Fatalf("test failed expected 10, got %d", ret)} else {t.Logf("store 10 add 1 ok, got %d", ret)}an1.Swap(100) // 使用100对原子数据进行交换, 交换后的结果为100if an1.Load() != 100 {t.Fatalf("test failed expected to be 100, got %v", an1.Load())} else {t.Logf("an1.Swap(100) ok, got %d", an1.Load())}// 比较并交换,这里会拿第一个参数的值和原子数进行比较,//  如果第一个参数的值和原子数相等,就会拿第二个参数的值对对原子数进行交换, 否则返回false,不进行交换swapped := an1.CompareAndSwap(10, 200) // 第一个参数 10 和当前原子数100比较,不会被交换if !swapped {t.Logf("atomic data not swapped, now data is %v, but param1 for compare is 10", an1.Load())} else {t.Fatalf("an1.CompareAndSwap(10, 200) fail, expected 100 got %d", an1.Load())}if an1.CompareAndSwap(100, 200) { // 会被交换// 现在原子的数据应该是 200t.Logf("atomic data swapped, now data is %v ", an1.Load())} else {t.Fatalf("atomic data swapped failed: %v", an1.Load())}/*// 单元测试结果 下面的参数 -v 表示输出测试日志 即 使用t.Log输出的内容✗ go test -run=^TestAtomicType$ -v=== RUN   TestAtomicTypetype_val_test.go:43: store 10 add 1 ok, got 11type_val_test.go:49: an1.Swap(100) ok, got 100type_val_test.go:55: atomic data not swapped, now data is 100, but param1 for compare is 10type_val_test.go:62: atomic data swapped, now data is 200--- PASS: TestAtomicType (0.00s)PASSok      atomic_demo    0.356s*/
}

另外2个官方示例就不贴了,原理都一样, 大家有兴趣的话可以自己去瞄瞄 pkg.go.dev/sync/atomic#example-Value-ReadMostly

怎么样,这个看似很神秘,其实也很简单的底层原子级内存操作是不是明白了?

atomic类型定义参考

type Bool
func (x *Bool) CompareAndSwap(old, new bool) (swapped bool)
func (x *Bool) Load() bool
func (x *Bool) Store(val bool)
func (x *Bool) Swap(new bool) (old bool)
type Int32
func (x *Int32) Add(delta int32) (new int32)
func (x *Int32) CompareAndSwap(old, new int32) (swapped bool)
func (x *Int32) Load() int32
func (x *Int32) Store(val int32)
func (x *Int32) Swap(new int32) (old int32)
type Int64
func (x *Int64) Add(delta int64) (new int64)
func (x *Int64) CompareAndSwap(old, new int64) (swapped bool)
func (x *Int64) Load() int64
func (x *Int64) Store(val int64)
func (x *Int64) Swap(new int64) (old int64)
type Pointer
func (x *Pointer[T]) CompareAndSwap(old, new *T) (swapped bool)
func (x *Pointer[T]) Load() *T
func (x *Pointer[T]) Store(val *T)
func (x *Pointer[T]) Swap(new *T) (old *T)
type Uint32
func (x *Uint32) Add(delta uint32) (new uint32)
func (x *Uint32) CompareAndSwap(old, new uint32) (swapped bool)
func (x *Uint32) Load() uint32
func (x *Uint32) Store(val uint32)
func (x *Uint32) Swap(new uint32) (old uint32)
type Uint64
func (x *Uint64) Add(delta uint64) (new uint64)
func (x *Uint64) CompareAndSwap(old, new uint64) (swapped bool)
func (x *Uint64) Load() uint64
func (x *Uint64) Store(val uint64)
func (x *Uint64) Swap(new uint64) (old uint64)
type Uintptr
func (x *Uintptr) Add(delta uintptr) (new uintptr)
func (x *Uintptr) CompareAndSwap(old, new uintptr) (swapped bool)
func (x *Uintptr) Load() uintptr
func (x *Uintptr) Store(val uintptr)
func (x *Uintptr) Swap(new uintptr) (old uintptr)
type Value
func (v *Value) CompareAndSwap(old, new any) (swapped bool)
func (v *Value) Load() (val any)
func (v *Value) Store(val any)
func (v *Value) Swap(new any) (old any)

 atomic函数的定义参考

这个看上去一大堆,其实下面这些个函数的定义官方都不建议使用! 在手册中你都能看到这样一句话“Consider using the more ergonomic and less error-prone xxx instead.”  都建议你使用更符合人体工程学的且不容易出错的 xxx 方法代替

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)
func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool)
func CompareAndSwapInt64(addr *int64, old, new int64) (swapped bool)
func CompareAndSwapPointer(addr *unsafe.Pointer, old, new unsafe.Pointer) (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 LoadInt32(addr *int32) (val int32)
func LoadInt64(addr *int64) (val int64)
func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer)
func LoadUint32(addr *uint32) (val uint32)
func LoadUint64(addr *uint64) (val uint64)
func LoadUintptr(addr *uintptr) (val uintptr)
func StoreInt32(addr *int32, val int32)
func StoreInt64(addr *int64, val int64)
func StorePointer(addr *unsafe.Pointer, val unsafe.Pointer)
func StoreUint32(addr *uint32, val uint32)
func StoreUint64(addr *uint64, val uint64)
func StoreUintptr(addr *uintptr, val uintptr)
func SwapInt32(addr *int32, new int32) (old int32)
func SwapInt64(addr *int64, new int64) (old int64)
func SwapPointer(addr *unsafe.Pointer, new unsafe.Pointer) (old unsafe.Pointer)
func SwapUint32(addr *uint32, new uint32) (old uint32)
func SwapUint64(addr *uint64, new uint64) (old uint64)
func SwapUintptr(addr *uintptr, new uintptr) (old uintptr)

参考文档 atomic package - sync/atomic - Go Packages

这篇关于一文搞明白golang底层原子级内存操作 的使用(sync atomic包)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1028974

相关文章

Spring IoC 容器的使用详解(最新整理)

《SpringIoC容器的使用详解(最新整理)》文章介绍了Spring框架中的应用分层思想与IoC容器原理,通过分层解耦业务逻辑、数据访问等模块,IoC容器利用@Component注解管理Bean... 目录1. 应用分层2. IoC 的介绍3. IoC 容器的使用3.1. bean 的存储3.2. 方法注

golang中reflect包的常用方法

《golang中reflect包的常用方法》Go反射reflect包提供类型和值方法,用于获取类型信息、访问字段、调用方法等,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值... 目录reflect包方法总结类型 (Type) 方法值 (Value) 方法reflect包方法总结

Python内置函数之classmethod函数使用详解

《Python内置函数之classmethod函数使用详解》:本文主要介绍Python内置函数之classmethod函数使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录1. 类方法定义与基本语法2. 类方法 vs 实例方法 vs 静态方法3. 核心特性与用法(1编程客

怎样通过分析GC日志来定位Java进程的内存问题

《怎样通过分析GC日志来定位Java进程的内存问题》:本文主要介绍怎样通过分析GC日志来定位Java进程的内存问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、GC 日志基础配置1. 启用详细 GC 日志2. 不同收集器的日志格式二、关键指标与分析维度1.

Linux中压缩、网络传输与系统监控工具的使用完整指南

《Linux中压缩、网络传输与系统监控工具的使用完整指南》在Linux系统管理中,压缩与传输工具是数据备份和远程协作的桥梁,而系统监控工具则是保障服务器稳定运行的眼睛,下面小编就来和大家详细介绍一下它... 目录引言一、压缩与解压:数据存储与传输的优化核心1. zip/unzip:通用压缩格式的便捷操作2.

Python实现对阿里云OSS对象存储的操作详解

《Python实现对阿里云OSS对象存储的操作详解》这篇文章主要为大家详细介绍了Python实现对阿里云OSS对象存储的操作相关知识,包括连接,上传,下载,列举等功能,感兴趣的小伙伴可以了解下... 目录一、直接使用代码二、详细使用1. 环境准备2. 初始化配置3. bucket配置创建4. 文件上传到os

Java内存分配与JVM参数详解(推荐)

《Java内存分配与JVM参数详解(推荐)》本文详解JVM内存结构与参数调整,涵盖堆分代、元空间、GC选择及优化策略,帮助开发者提升性能、避免内存泄漏,本文给大家介绍Java内存分配与JVM参数详解,... 目录引言JVM内存结构JVM参数概述堆内存分配年轻代与老年代调整堆内存大小调整年轻代与老年代比例元空

使用Python实现可恢复式多线程下载器

《使用Python实现可恢复式多线程下载器》在数字时代,大文件下载已成为日常操作,本文将手把手教你用Python打造专业级下载器,实现断点续传,多线程加速,速度限制等功能,感兴趣的小伙伴可以了解下... 目录一、智能续传:从崩溃边缘抢救进度二、多线程加速:榨干网络带宽三、速度控制:做网络的好邻居四、终端交互

Python中注释使用方法举例详解

《Python中注释使用方法举例详解》在Python编程语言中注释是必不可少的一部分,它有助于提高代码的可读性和维护性,:本文主要介绍Python中注释使用方法的相关资料,需要的朋友可以参考下... 目录一、前言二、什么是注释?示例:三、单行注释语法:以 China编程# 开头,后面的内容为注释内容示例:示例:四

mysql表操作与查询功能详解

《mysql表操作与查询功能详解》本文系统讲解MySQL表操作与查询,涵盖创建、修改、复制表语法,基本查询结构及WHERE、GROUPBY等子句,本文结合实例代码给大家介绍的非常详细,感兴趣的朋友跟随... 目录01.表的操作1.1表操作概览1.2创建表1.3修改表1.4复制表02.基本查询操作2.1 SE