

Go 原子操作
source link: https://studygolang.com/articles/19638?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

概念
原子操作,意思就是执行的过程不能背终端的操作。在针对某个值的原子操作执行过程中,cpu不会再去执行其他针对这个值得操作。在底层, 这会由CPU提供芯片级别的支持 ,所以绝对有效。即使在拥有多CPU核心,或者多CPU的计算机系统中,原子操作的保证也是不可撼动的。
Go语言提供了院子操作的包atomic。其中有很多函数可以帮助我们进行原子操作。但是只能对几种简单类型进行原子操作:int32、int64、uint32、uint64、uintptr和unsafe.Ponter。atomic为这些简单类型童工了5中操作函数:增或减、比较并交换、载入、存储和交换。
为什么选择原子操作
我们知道go语言在sync包中提供了锁的包,但是为什么我们还要使用atomic原子操作呢?总结下来有一下几个原因:
- 加锁的代价比较耗时,需要上下文切换。即使是在go语言的goroutine中也需要上下文的切换
- 只是针对基本类型,可以使用原子操作保证线程安全
- 原子操作在用户态可以完成,性能比互斥锁要高
- 针对特定需求,原子操作的步骤简单,不需要加锁-操作-解锁 这样的步骤
五种操作
一下5中操作例子都是用uint64来写
增或减
针对以上6种简单类型,atomic包支持院子增/减的操作函数。
var i64 uint64 //第一个参数必须是指针 atomic.AddUint64(&i64,5) //在uint类型中可以使用^uint64(0)的方式打到减的效果 atomic.AddUint64(&i64, ^uint64(0)) fmt.Println(i64)
比较并交换(CAS-Compare And Swap)
var i64 uint64 i64 = 5 // cas接受3个参数,第一个为需要替换值得指针,第二个为旧值,第三个为新值 // 当指针指向的值,跟你传递的旧值相等的情况下 指针指向的值会被替换 ok := atomic.CompareAndSwapUint64(&i64,5, 50) fmt.Println(ok) // 当指针指向的值跟传递的旧值不相等,则返回false ok = atomic.CompareAndSwapUint64(&i64,40, 50) fmt.Println(ok)
载入
var i64 uint64 i64 = 1 //load 函数接收一个指针类型 返回指针指向的值 num := atomic.LoadUint64(&i64) fmt.Println(num)
存储
var i64 uint64 i64 = 1 //store 函数接受一个指针类型和一个值 函数将会把值赋到指针地址中 atomic.StoreUint64(&i64, 5) fmt.Println(i64)
交换
var i64 uint64 i64 = 1 //swap接受一个指针 一个值。函数会把值赋给指针 并返回旧值 old := atomic.SwapUint64(&i64, 5) fmt.Println("old:",old,"new:",i64)
原子值
原子值可接受的备操作值得类型不限,这意味着我们可以把任何类型的值放入原子值。原子值只有2个公开的方法:Load、Store。一个是获取另一个是存储。
下面来看下简单操作:
var countVal atomic.Value //store函数 接受interface 并存储 countVal.Store([]int{1,2,3,4,5}) //load函数 返回 atomic.value中的值 list := countVal.Load().([]int) fmt.Println(list)
下面有一个并发安全的int数组的例子
package main import ( "errors" "sync/atomic" ) func main() { } // ConcurrentArray 代表并发安全的整数数组接口。 type ConcurrentArray interface { // Set 用于设置指定索引上的元素值。 Set(index uint32, elem int) (err error) // Get 用于获取指定索引上的元素值。 Get(index uint32) (elem int, err error) // Len 用于获取数组的长度。 Len() uint32 } type MyArray struct { val atomic.Value length uint32 } func (array *MyArray)CheckValue()(err error){ if array.val.Load() == nil{ errors.New("array is empty") } return nil } func (array *MyArray)CheckIndex(index uint32)(error){ if array.length <= index{ errors.New("array out of the range") } return nil } func (m *MyArray)Set(index uint32, elem int)(err error){ if err := m.CheckValue();err != nil{ return err } if err = m.CheckIndex(index);err!=nil{ return err } newArray := make([]int, m.length) copy(newArray ,m.val.Load().([]int)) newArray[index] = elem m.val.Store(newArray) return nil } func (array *MyArray)Get(index uint32) (elem int, err error){ if err := array.CheckValue();err != nil{ return 0,err } if err = array.CheckIndex(index);err!=nil{ return 0,err } num := array.val.Load().([]int)[index] return num, err }
Recommend
-
65
-
61
前言 对于Java多线程,接触最多的莫过于使用synchronized,这个简单易懂,但是这synchronized并非性能最优的。今天我就简单介绍一下几种锁。可能我...
-
49
一:原子操作CAS(compare-and-swap) 原子操作分三步:读取addr的值,和old进行比较,如果相等,则将new赋值给*addr,他能保证这三步一起执行完成,叫原子操作也就是说它不能再分了,当有一个CPU在访问这块内容addr时,其他CPU就...
-
88
原子操作 对于一个Go程序来说,GO语言运行时系统中的调度器会恰当的安排其中所有的goroutine的运行。不过,在同一时刻,只会有少数的goroutine真正处于运行状态。为了公平起见,调度器会频繁的切换这些goroutine。这个中断的...
-
36
这个系列的文章里介绍了很多并发编程里经常用到的技术,除了 Context 、计时器、互斥锁还有通道外还有一种技术-- 原子操作 在一些同步算法中会被用到。今天的文章里我们会简单了解一下 Go...
-
22
前言 所谓原子操作,就是要么不做,要么全做。在很多场景中,都有对原子操作的需求。在翻aep的spec文档时...
-
14
Linux学习第27节,内核中的原子操作 发表于 2019-03-01 20:03:53...
-
11
stdatomic 已经是 C11 的标准,并且成为了 C++ 标准的一部分。
-
7
32位系统下,Go标准库中atomic原子操作int64有崩溃bug 发表于 2021-03-12 | 分类于 Go| 字数统计: 714下面这个demo,在32位系统(我测试的运行环境:32位arm linux)会崩...
-
4
Java 中 long 是不是原子操作?发布于 8 月 25 日Java中long和double的原子性java中基本类型中,long和double的长度都是8个字节,32位(4...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK