Golang如何使用sync/atomic实现原子操作

来源:前端技术作者:孙悟空头衔:草根站长
导读:本期聚焦于小伙伴创作的《Golang如何使用sync/atomic实现原子操作》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《Golang如何使用sync/atomic实现原子操作》有用,将其分享出去将是对创作者最好的鼓励。

在Golang的并发编程场景中,多个goroutine同时读写同一个共享变量时,很容易出现数据竞争的问题,导致程序运行结果不符合预期。传统的解决方式是使用互斥锁或者读写锁来保证同一时间只有一个goroutine能操作共享变量,但锁的加解锁过程会带来额外的性能开销。而sync/atomic包提供了一系列底层的原子操作函数,能够直接对基础类型的变量进行原子性的修改、读取、交换等操作,不需要加锁就能保证并发安全,在很多轻量级的并发场景下能显著提升程序性能。

Golang如何使用sync/atomic实现原子操作

sync/atomic支持的基础类型

sync/atomic包主要针对以下几类基础类型提供原子操作支持:

  • int32、int64、uint32、uint64、uintptr等整数类型
  • unsafe.Pointer指针类型
  • Go 1.19之后新增的atomic.Bool、atomic.Int32、atomic.Int64等泛型原子类型

常用原子操作函数及使用示例

1. 原子读取和写入

普通的变量读取和写入在并发场景下可能不是原子的,使用atomic.Load和atomic.Store系列函数可以保证操作的原子性。

package main

import (
	"fmt"
	"sync"
	"sync/atomic"
)

var count int64

func main() {
	var wg sync.WaitGroup
	// 启动10个goroutine并发修改count
	for i := 0; i < 10; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			// 原子增加count的值
			atomic.AddInt64(&count, 1)
		}()
	}
	wg.Wait()
	// 原子读取count的值
	fmt.Println("最终count值:", atomic.LoadInt64(&count))
}

2. 原子增减操作

atomic.Add系列函数可以对整数类型进行原子性的加减操作,返回修改后的值。

package main

import (
	"fmt"
	"sync"
	"sync/atomic"
)

func main() {
	var num int32 = 10
	// 原子加5
	newVal := atomic.AddInt32(&num, 5)
	fmt.Println("加5后的值:", newVal) // 输出15
	// 原子减3,传入负数即可
	newVal = atomic.AddInt32(&num, -3)
	fmt.Println("减3后的值:", newVal) // 输出12
}

3. 原子交换和比较并交换

atomic.Swap系列函数可以直接交换变量的值,返回旧值;atomic.CompareAndSwap(简称CAS)系列函数会比较变量的当前值是否等于预期值,如果相等则替换为新值,返回是否替换成功。

package main

import (
	"fmt"
	"sync/atomic"
)

func main() {
	var val int64 = 20
	// 原子交换,将val设为100,返回旧值20
	old := atomic.SwapInt64(&val, 100)
	fmt.Println("交换前的旧值:", old) // 输出20
	fmt.Println("交换后的新值:", val) // 输出100

	// CAS操作,预期val是100,替换为200
	swapped := atomic.CompareAndSwapInt64(&val, 100, 200)
	fmt.Println("CAS是否成功:", swapped) // 输出true
	fmt.Println("CAS后的值:", val)      // 输出200

	// 再次CAS,预期val是100,实际是200,替换失败
	swapped = atomic.CompareAndSwapInt64(&val, 100, 300)
	fmt.Println("第二次CAS是否成功:", swapped) // 输出false
	fmt.Println("第二次CAS后的值:", val)      // 输出200
}

Go 1.19之后的泛型原子类型

Go 1.19引入了泛型原子类型,使用起来比之前的全局函数更加简洁,不需要手动传递指针,也不容易写错类型。

package main

import (
	"fmt"
	"sync"
	"sync/atomic"
)

func main() {
	// 使用atomic.Int64类型
	var counter atomic.Int64
	var wg sync.WaitGroup
	for i := 0; i < 5; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			counter.Add(1) // 原子加1
		}()
	}
	wg.Wait()
	fmt.Println("counter最终值:", counter.Load()) // 输出5

	// 使用atomic.Bool类型
	var flag atomic.Bool
	flag.Store(true) // 原子设置为true
	fmt.Println("flag当前值:", flag.Load()) // 输出true
}

原子操作与互斥锁的对比

原子操作和互斥锁都能保证并发安全,但适用场景和性能表现有所不同,具体对比如下:

对比维度原子操作互斥锁
适用场景简单的基础类型变量的读写、修改操作复杂的逻辑操作、多变量同时修改的场景
性能开销底层硬件指令实现,开销极小需要加解锁、可能的协程调度,开销较大
功能复杂度仅支持简单的原子操作,不支持复杂逻辑可以保护任意复杂的代码块
死锁风险如果加解锁逻辑错误可能出现死锁

使用原子操作的注意事项

  • 原子操作仅适用于sync/atomic支持的基础类型,不能直接用于结构体、切片、映射等复杂类型。
  • 不要对同一个变量混合使用原子操作和非原子操作,否则仍然会出现数据竞争问题。
  • CAS操作在高竞争场景下可能会出现大量失败重试,此时性能可能不如互斥锁,需要根据实际场景测试选择。
  • 使用unsafe.Pointer的原子操作时,需要保证指针指向的内存生命周期正确,避免出现悬垂指针。
原子操作是Golang并发编程中轻量级并发控制的重要手段,合理使用能够在不牺牲并发安全的前提下显著提升程序性能,开发者需要根据操作复杂度和并发场景选择合适的并发控制方案。

Golangsync/atomic原子操作并发安全性能优化修改时间:2026-07-02 09:03:40

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。