了解 Go 中原子操作的重要性与使用方法

news/2024/12/6 18:22:16/

引言

并发是现代软件开发的一个基本方面,而在 Go 中编写并发程序相对来说是一个相对轻松的任务,这要归功于其强大的并发支持。
Go 提供了对原子操作的内置支持,这在同步并发程序中起着至关重要的作用。在本篇博客文章中,我们将探索 Go 中原子操作的概念,了解为什么它们是重要的,以及如何有效地使用它们。

什么是 Go 中的原子操作?

在 Go 中,原子操作是无需中断或受其他并发操作干扰而执行的操作。它们用于确保对共享变量的某些操作被原子地执行,这意味着它们作为一个单一的、不可分割的单元执行,并且不受其他 goroutine 或线程的干扰或数据竞争的影响。
Go 提供了一个名为 sync/atomic 的包,其中包含一组用于对原始数据类型(如整数和指针)执行原子操作的函数。在 Go 中,一些常用的原子操作包括:

  • Load(加载)
    atomic.Load* 函数用于原子地读取变量的值。例如,atomic.LoadInt32 用于原子地加载 int32 变量的值。

  • Store(存储)
    atomic.Store* 函数用于原子地设置变量的值。例如,atomic.StoreInt32 用于原子地设置 int32 变量的值。

  • Add 和 Subtract(增加和减少)
    atomic.Add* 和 atomic.Sub* 函数用于原子地增加或减少变量的值。

  • Compare and Swap(CAS,比较并交换)
    atomic.CompareAndSwap* 函数用于原子地比较变量的当前值与期望值,并在它们匹配时将变量设置为一个新值。这通常用于实现无锁的数据结构和算法。

  • Swap(交换)
    atomic.Swap* 函数用于原子地交换变量的值与一个新值。

这些原子操作在并发环境中与共享变量一起使用时非常有价值,可以防止数据竞争,并确保对变量的操作安全且一致地执行。它们有助于构建并发数据结构、同步原语以及以线程安全的方式管理共享资源。

使用这些操作时是否需要互斥锁?

在 Go 中,sync/atomic 包提供了原子操作,可以在没有互斥锁的情况下对共享变量进行原子更新。使用原子操作的主要优势是它们通常比传统的互斥锁更高效,特别是对于像整数和指针这样的简单的原始数据类型的简单操作。
使用原子操作时不需要互斥锁,因为这些操作被设计为线程安全的,并且可以在不需要显式锁定和解锁互斥锁的情况下进行原子更新。原子操作在硬件级别上操作,确保操作的原子性,防止数据竞争,并避免传统锁定机制的需求。
然而,需要注意的是,原子操作也有其局限性。它们最适合用于对简单的、低级别的原始数据类型进行简单的更新。如果需要执行涉及多个变量或需要更复杂的同步的更复杂操作,则可能仍然需要使用互斥锁或其他同步原语。
总之,虽然原子操作可以在简单的原子更新共享变量的情况下不使用互斥锁,但是在选择原子操作和互斥锁之间取决于具体任务的需求和复杂性。根据并发代码的具体需求,选择合适的同步机制非常重要。
示例代码

package mainimport ("fmt""sync/atomic""time"
)func main() {var counter int32// 创建一个 goroutine 来增加计数器的值。go func() {for i := 0; i < 5; i++ {atomic.AddInt32(&counter, 1)fmt.Printf("增加: %d\\n", atomic.LoadInt32(&counter))time.Sleep(time.Millisecond)}}()// 创建一个 goroutine 来减少计数器的值。go func() {for i := 0; i < 5; i++ {atomic.AddInt32(&counter, -1)fmt.Printf("减少: %d\\n", atomic.LoadInt32(&counter))time.Sleep(time.Millisecond)}}()// 等待 goroutine 结束。time.Sleep(2 * time.Second)fmt.Printf("最终值: %d\\n", atomic.LoadInt32(&counter))
}

行以上示例代码,我们可以看到一个类型为 int32 的共享计数器变量。
创建了两个 goroutine,一个用于增加计数器的值,另一个用于减少计数器的值。我们使用 atomic.AddInt32 来原子地增加或减少计数器的值。我们使用 atomic.LoadInt32 来安全地加载计数器的值以供打印。程序使用 time.Sleep 等待 goroutine 结束。使用原子操作可以确保计数器在没有互斥锁的情况下安全地更新。你应该看到计数器在没有竞争的情况下正确地增加和减少。
该事件序列演示了操作的正确交错,最终计数器的值为 0。
这个输出证实了原子操作的工作方式,确保共享数据的安全性,而无需使用互斥锁进行同步。

结论

在 Go 中,原子操作是确保并发程序正确性和性能的重要工具。通过允许对共享内存进行安全操作,它们使开发人员能够编写高效可靠的并发代码。然而,在处理 Go 应用程序中的并发时,合理使用原子操作并了解潜在的权衡是非常重要的。通过对原子操作有着扎实的理解并正确使用,您可以构建健壮且响应迅速的并发程序。


http://www.ppmy.cn/news/1365103.html

相关文章

最新红盟云卡个人自动发卡开源系统源码优化版

红盟云卡系统是云商学院旗下的一款基于 PHPMySQL 开发的虚拟商品在线售卖平台。它是一款漂亮且功能丰富的发卡网站&#xff0c;可以与社区进行对接。该系统完全开源且无任何加密&#xff0c;可商业使用&#xff0c;并支持个人免签多个接口。 下载地址&#xff1a;优化版.zip

七、ChatGPT为什么会被热炒?

2023年上半年&#xff0c;ChatGPT引起了广泛的热议&#xff0c;对于ChatGPT有多热&#xff0c;不需要我重复了&#xff0c;你可能在网上看到了很多报道&#xff0c;标题如《ChatGPT揭开AI战幔&#xff1a;杀死黄页一样摧毁Google&#xff1f;》和《ChatGPT强势来袭&#xff0c;…

vscode右键菜单栏功能说明

本文主要介绍在vscode中的python代码文件中&#xff0c;单击鼠标右键出现的菜单栏功能。部分功能可能与安装插件相关&#xff0c;主要用于个人查阅。 单击右键菜单栏如下&#xff1a; GO to xx类型命令 “Go to Definition”、“Go to Declaration”、"Go to Type Defin…

python_pyecharts_柱形图

from pyecharts.charts import Bar from pyecharts import options as opts # 创建一个柱形图实例 bar Bar() # 设置x轴数据 bar.add_xaxis(["A", "B", "C", "D", "E"]) # 设置y轴数据 bar.add_yaxis("柱形图", …

音频smmu问题之smmu学习

一、音频smmu 内存访问问题 在工作中&#xff0c;遇到一个smmu问题&#xff0c;主要log信息如下&#xff1a; arm-smmu 15000000.apps-smmu: Unhandled arm-smmu context fault from soc:spf_core_platform:qcom,msm-audio-ion! arm-smmu 15000000.apps-smmu: FAR 0x0000000…

devc++跑酷小游戏3.5.0

本来想搞存档的&#xff0c;失败了&#xff0c;要再学学文件操作的函数。还有一个打印地图的函数&#xff0c;更失败&#xff0c;彻底放弃。最近开学了&#xff0c;游戏不会经常更新&#xff0c;要写作业。昨天写到10点T_T #include<bits/stdc.h> #include<windows.h…

vue-router4 (二) 引入并配置路由

1.在项目src/router/index.ts 文件夹下配置路由&#xff1a; import { createRouter ,createWebHistory,RouteRecordRaw} from "vue-router"; //1.引入路由//3.routes配置项 const routes:Array<RouteRecordRaw> [{path:"/", //路径name:"…

【GameFramework框架内置模块】4、内置模块之调试器(Debugger)

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址QQ群&#xff1a;398291828 大家好&#xff0c;我是佛系工程师☆恬静的小魔龙☆&#xff0c;不定时更新Unity开发技巧&#xff0c;觉得有用记得一键三连哦。 一、前言 【GameFramework框架】系列教程目录&#xff1a;…