Go原子操作 sync/atomic

sync/atomic包提供了底层的原子级内存操作,其执行过程不能被中断,这也就保证了同一时刻一个线程的执行不会被其他线程中断,也保证了多线程下数据操作的一致性。
操作的数据类型共有六种:int32, int64, uint32, uint64, uintptr, unsafe.Pinter,每一种类型又提供了五种操作:Add增减, CompareAndSwap比较并交换, Swap交换, Load载入, Store存储。
函数名以"操作+类型"组合而来。例如AddInt32/AddUint64/LoadInt32/LoadUint64...

以Int32为例:

// AddInt32 atomically adds delta to *addr and returns the new value.
func AddInt32(addr *int32, delta int32) (new int32)
AddInt32可以实现对元素的原子增加或减少,函数会直接在传递的地址上进行修改操作。
addr需要修改的变量的地址,delta修改的差值[正或负数],返回new修改之后的新值。

var a int32 
a += 10 
atomic.AddInt32(&a, 10) 
fmt.Println(a == 20) // true

//需要注意的是如果是uint32,unint64时,不能直接传负数,所以需要利用二进制补码机制
var b uint32
b += 20 
atomic.AddUint32(&b, ^uint32(10-1)) // 等价于 b -= 10 
// atomic.Adduint32(&b, ^uint32(N-1)) //N为需要减少的正整数值
fmt.Println(b == 10) // true

 

// CompareAndSwapInt32 executes the compare-and-swap operation for an int32 value.
func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool)
函数会先判断参数addr指向的值与参数old是否相等,如果相等,则用参数new替换参数addr的值。最后返回swapped是否替换成功。

package main
import (
	"fmt"
	"sync"
	"sync/atomic"
)
func main() {
	var c int32
	wg := sync.WaitGroup{}
	//开启100个goroutine
	for i := 0; i < 100; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			tmp := atomic.LoadInt32(&c)
			if !atomic.CompareAndSwapInt32(&c, tmp, tmp+1) {
				fmt.Println("c 修改失败")
			}
		}()
	}
	wg.Wait()
	//c的值有可能不等于100,频繁修改变量值情况下,CompareAndSwap操作有可能不成功。
	fmt.Println("c : ", c)
}
偶尔输出:
//c 修改失败
//c :  99
多数情况下输出
//c :  100

 

// SwapInt32 atomically stores new into *addr and returns the previous *addr value.
func SwapInt32(addr *int32, new int32) (old int32)
Swap会直接执行赋值操作,并将原值作为返回值返回

package main
import (
	"fmt"
	"sync"
	"sync/atomic"
)
func main() {
	var e int32
	wg2 := sync.WaitGroup{}
	//开启10个goroutine
	for i := 0; i < 10; i++ {
		wg2.Add(1)
		go func() {
			defer wg2.Done()
			tmp := atomic.LoadInt32(&e)
			old := atomic.SwapInt32(&e, tmp + 1)
			fmt.Println("e old : ", old)
		}()
	}
	wg2.Wait()
	fmt.Println("e : ", e)
}

// e old :  3
// e old :  1
// e old :  2
// e old :  4
// e old :  5
// e old :  6
// e old :  7
// e old :  8
// e old :  9
// e old :  0
// e :  10

 

// LoadInt32 atomically loads *addr.
func LoadInt32(addr *int32) (val int32)
Load函数参数为需要读取的变量地址,返回值为读取的值
Load和Store操作对应与变量的原子性读写,许多变量的读写无法在一个时钟周期内完成,而此时执行可能会被调度到其他线程,无法保证并发安全。Load只保证读取的不是正在写入的值,Store只保证写入是原子操作。所以在使用的时候要注意。

 

 

// StoreInt32 atomically stores val into *addr.
func StoreInt32(addr *int32, val int32)
Store函数参数为需要存储的变量地址及需要写入的值,存储某个值时,任何CPU都不会对该值进行读或写操作,存储操作总会成功,它不关心旧值是什么,与CompareAndSwap不同

var d int32
fmt.Println("d : ", d)
atomic.StoreInt32(&d, 666)
fmt.Println("d : ", d)

 

文章来源于 https://www.lixiaocheng.com/read/704

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了小程序应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值