概念
原子操作,意思就是执行的过程不能背终端的操作。在针对某个值的原子操作执行过程中,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
}
有疑问加站长微信联系(非本文作者)