前面几篇文章介绍了Golang中互斥锁、读写锁、条件变量,虽然它们可以很好地协调对共享资源的访问,但并不能保证原子操作。
原子操作是指一系列操作要么全部执行成功,要么全部执行失败,不会有中间状态。
锁无法保证原子性是因为:
可以看出原子操作的粒度更细,它对单个变量的访问进行了原子化保证,在操作完成之前会阻塞其他并发操作。能够保证原子性执行的只有原子操作,原子操作在执行过程中是不允许被中断的。在计算机底层,原子性是由CPU支持的,所以绝对有效。Golang中的原子操作是基于操作系统和CPU的,具体功能由标准库中的sync/atomic包提供。
sync/atomic包提供的原子操作有Add、Load、Store、Swap和CompareAndSwap,这些函数支持的数据类型有int32、int64、uint32、uint64、uintptr和unsafe包中的Pointer,不过,没有提供针对unsafe.Pointer的Add方法。具体方法如下:
此外,sync/atomic包还提供一个名称为Value的类型,可以被用来存储任意类型的值,结构如下:
type Value struct {
v any
}
使用原子操作可以用于计算需要在多个goroutine之间共享的计数器。例如,计算在线用户数量、任务完成情况等等。
package main
import (
"fmt"
"sync/atomic"
)
func main() {
var counter int64
done := make(chan bool)
for i := 0; i < 100; i++ {
go func() {
atomic.AddInt64(&counter, 1)
done <- true
}()
}
for i := 0; i < 100; i++ {
<-done
}
fmt.Println(counter)
}
首先声明了一个int64类型的计数器变量counter,使用AddInt64原子操作对其进行递增。通过使用AddInt64,确保了每个goroutine对其值的修改操作都能够安全进行。
再看一个自旋锁的示例:
package main
import (
"fmt"
"sync/atomic"
"time"
)
func main() {
sign := make(chan struct{}, 2)
var num int32
go func() {
defer func() {
sign <- struct{}{}
}()
for {
// 定时增大num值
time.Sleep(time.Millisecond * 500)
newNum := atomic.AddInt32(&num, 2)
fmt.Printf("num当前值为: %d\n", newNum)
// 满足条件后退出
if newNum == 10 {
break
}
}
}()
go func() {
// 定时检查num值,等于10则归零
defer func() {
sign <- struct{}{}
}()
for {
if atomic.CompareAndSwapInt32(&num, 10, 0) {
fmt.Println("已将num归零")
break
}
time.Sleep(time.Millisecond * 500)
}
}()
<-sign
<-sign
}
分享名称:Golang中的同步工具原子操作详解
网站网址:http://www.shufengxianlan.com/qtweb/news24/338524.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联