Golang标准库学习——sync/atomic

package atomic

import "sync/atomic"

atomic包提供了底层的原子级内存操作,对于同步算法的实现很有用。

这些函数必须谨慎地保证正确使用。除了某些特殊的底层应用,使用通道或者sync包的函数/类型实现同步更好。

应通过通信来共享内存,而不通过共享内存实现通信。

相比于使用锁,在条件允许的情况下,使用原子操作的效率会更高

atomic包能对一些类型进行原子操作,包括int32, int64, uint32, uint64, uintptr, unsafe.Pointer六种类型。原子操作包括增或减、交换、比较并交换、载入、存储五种操作。

一、载入

载入包含以下六个函数,分别是对上述六种数据类型的操作。

func LoadInt32(addr *int32) (val int32)
func LoadInt64(addr *int64) (val int64)
func LoadUint32(addr *uint32) (val uint32)
func LoadUint64(addr *uint64) (val uint64)
func LoadUintptr(addr *uintptr) (val uintptr)
func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer)

载入函数是使我们能够原子性的获取*addr的值,代表变量的原子性读。变量的读可能在一个时钟周期内无法完成,无法保证并发安全。

二、存储

存储包含以下六个函数,对应六种数据类型。

func StoreInt32(addr *int32, val int32)
func StoreInt64(addr *int64, val int64)
func StoreUint32(addr *uint32, val uint32)
func StoreUint64(addr *uint64, val uint64)
func StoreUintptr(addr *uintptr, val uintptr)
func StorePointer(addr *unsafe.Pointer, val unsafe.Pointer)

存储函数是使我们能够原子性的将val值保存到*addr中。代表变量的原子性写。变量的写可能在一个时钟周期内无法完成,无法保证并发安全。

三、增或减

存储包含以下五个函数,对应五种数据类型。

func AddInt32(addr *int32, delta int32) (new int32)
func AddInt64(addr *int64, delta int64) (new int64)
func AddUint32(addr *uint32, delta uint32) (new uint32)
func AddUint64(addr *uint64, delta uint64) (new uint64)
func AddUintptr(addr *uintptr, delta uintptr) (new uintptr)

增减函数是使我们能够原子性的将val值添加到*addr中,并返回这个新值。如下:

func main() {
    var a int32
    var b uint32
    a += 10
    atomic.AddInt32(&a, -10)
    fmt.Println(a)    // a=0
    b += 20
    atomic.AddIne32(&b, ^uint32(10-1))
    fmt.Println(b)    // b=10
)

尽管名字是Add,但是也可以实现减操作,特别注意uint无符号类型的减法操作。 

四、交换

存储包含以下五个函数,对应五种数据类型。

func SwapInt64(addr *int64, new int64) (old int64)
func SwapUint32(addr *uint32, new uint32) (old uint32)
func SwapUint64(addr *uint64, new uint64) (old uint64)
func SwapUintptr(addr *uintptr, new uintptr) (old uintptr)
func SwapPointer(addr *unsafe.Pointer, new unsafe.Pointer) (old unsafe.Pointer)

交换函数是使我们能够原子性的将val值保存到*addr中,并返回addr的旧值。原子操作不会关心旧值,只是单纯的返回。

五、比较并交换

存储包含以下六个函数,对应六种数据类型。

func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool)
func CompareAndSwapInt64(addr *int64, old, new int64) (swapped bool)
func CompareAndSwapUint32(addr *uint32, old, new uint32) (swapped bool)
func CompareAndSwapUint64(addr *uint64, old, new uint64) (swapped bool)
func CompareAndSwapUintptr(addr *uintptr, old, new uintptr) (swapped bool)
func CompareAndSwapPointer(addr *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool)

比较交换函数是使我们能够原子性的比较*addr和old,如果相同则将new赋值给*addr并返回真。如下:

var value int32

func main()  {
    fmt.Println("旧值: ", value)
    addValue(3)
    fmt.Println("CAS操作: ", value)
}

//不断地尝试原子地更新value的值,直到操作成功为止
func addValue(delta int32){
    //在被操作值被频繁变更的情况下,CAS操作并不那么容易成功
    //so 不得不利用for循环以进行多次尝试
    for {
        v := value
        if atomic.CompareAndSwapInt32(&value, v, (v + delta)){
            //在函数的结果值为true时,退出循环
            break
        }
        //操作失败的缘由总会是value的旧值已不与v的值相等了.
        //CAS操作虽然不会让某个Goroutine阻塞在某条语句上,但是仍可能会使流产的执行暂时停一下,不过时间大都极其短暂.
    }
}

 

参考资料:

1. https://studygolang.com/pkgdoc

2. https://studygolang.com/articles/3557

 


记录每天解决的小问题,积累起来去解决大问题。

你可能感兴趣的:(Golang基础学习)