We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
啥是原子操作呢?顾名思义,原子操作就是具备原子性的操作... 是不是感觉说了跟没说一样,原子性的解释如下:
一个或者多个操作在 CPU 执行的过程中不被中断的特性,称为原子性(atomicity) 。这些操作对外表现成一个不可分割的整体,他们要么都执行,要么都不执行,外界不会看到他们只执行到一半的状态。
Go语言通过内置包sync/atomic提供了对原子操作的支持,其提供的原子操作有以下几大类:
Go
sync/atomic
AddXXXType
LoadXXXType
Store
CompareAndSwap
CAS
Mutex
atomic
lock-free
CPU
func AtomicAdd() { var a int32 = 0 var wg sync.WaitGroup start := time.Now() for i := 0; i < 1000000; i++ { wg.Add(1) go func() { defer wg.Done() atomic.AddInt32(&a, 1) }() } wg.Wait() timeSpends := time.Now().Sub(start).Nanoseconds() fmt.Printf("use atomic a is %d, spend time: %v\n", atomic.LoadInt32(&a), timeSpends) }
完整源码,请访问atomic示例一
需要注意的是,所有原子操作方法的被操作数形参必须是指针类型,通过指针变量可以获取被操作数在内存中的地址,从而施加特殊的CPU指令,确保同一时间只有一个goroutine能够进行操作。
该操作简称CAS (Compare And Swap)。 这类操作的前缀为 CompareAndSwap :
func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool) func CompareAndSwapPointer(addr *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool) ......
该操作在进行交换前首先确保被操作数的值未被更改,即仍然保存着参数 old 所记录的值,满足此前提条件下才进行交换操作。CAS的做法类似操作数据库时常见的乐观锁机制。
old
atomic包里提供了一套Store开头的方法,用来保证各种类型变量的并发写安全,避免其他操作读到了修改变量过程中的脏数据。
func StoreInt32(addr *int32, val int32) func StoreInt64(addr *int64, val int64) func StorePointer(addr *unsafe.Pointer, val unsafe.Pointer) ...
这些操作方法的定义与上面介绍的那些操作的方法类似,就不再演示怎么使用这些方法了。值得一提的是如果你想要并发安全的设置一个结构体的多个字段,除了把结构体转换为指针,通过StorePointer设置外,还可以使用atomic包后来引入的atomic.Value,它在底层为我们完成了从具体指针类型到unsafe.Pointer之间的转换。
StorePointer
atomic.Value
unsafe.Pointer
有了atomic.Value后,它使得我们可以不依赖于不保证兼容性的unsafe.Pointer类型,同时又能将任意数据类型的读写操作封装成原子性操作(中间状态对外不可见)。
atomic.Value类型对外暴露了两个方法:
v.Store(c)
c
v
c := v.Load()
1.17 版本我看还增加了Swap和CompareAndSwap方法。
Swap
下面是一个简单的例子演示atomic.Value的用法。
type Rectangle struct { length int width int } var rect atomic.Value func update(width, length int) { rectLocal := new(Rectangle) rectLocal.width = width rectLocal.length = length rect.Store(rectLocal) } func main() { wg := sync.WaitGroup{} wg.Add(10) // 10 个协程并发更新 for i := 0; i < 10; i++ { go func() { defer wg.Done() update(i, i+5) }() } wg.Wait() _r := rect.Load().(*Rectangle) fmt.Printf("rect.width=%d\nrect.length=%d\n", _r.width, _r.length) }
完整源代码请访问:atomic示例二
你也可以试试,不用atomic.Value,直接给Rectange类型的指针变量赋值,看看在并发条件下,两个字段的值是不是能跟预期的一样变成10和15。
Rectange
原子操作由底层硬件支持,而锁则由操作系统的调度器实现。锁应当用来保护一段逻辑,对于一个变量更新的保护,原子操作通常会更有效率,并且更能利用计算机多核的优势,如果要更新的是一个复合对象,则应当使用atomic.Value封装好的实现。
The text was updated successfully, but these errors were encountered:
No branches or pull requests
啥是原子操作呢?顾名思义,原子操作就是具备原子性的操作... 是不是感觉说了跟没说一样,原子性的解释如下:
Go 语言提供了哪些原子操作
Go
语言通过内置包sync/atomic
提供了对原子操作的支持,其提供的原子操作有以下几大类:AddXXXType
LoadXXXType
Store
开头CompareAndSwap
开通,也就是CAS
,像Go
的很多并发原语实现就是依赖的CAS
操作Store
开头的操作方法,这个简单粗暴一些,不比较直接交换,一般不怎么用这个操作互斥锁跟原子操作的区别
Mutex
由操作系统的调度器实现,而atomic
包中的原子操作则由底层硬件指令直接提供支持,这些指令在执行的过程中是不允许中断的,因此原子操作可以在lock-free
的情况下保证并发安全,并且它的性能也能做到随CPU
个数的增多而线性扩展。原子操作增加、载入
完整源码,请访问atomic示例一
需要注意的是,所有原子操作方法的被操作数形参必须是指针类型,通过指针变量可以获取被操作数在内存中的地址,从而施加特殊的CPU指令,确保同一时间只有一个goroutine能够进行操作。
比较并交换
该操作简称
CAS
(Compare And Swap)。 这类操作的前缀为CompareAndSwap
:该操作在进行交换前首先确保被操作数的值未被更改,即仍然保存着参数
old
所记录的值,满足此前提条件下才进行交换操作。CAS
的做法类似操作数据库时常见的乐观锁机制。atomic.Value保证任意值的读写安全
atomic
包里提供了一套Store
开头的方法,用来保证各种类型变量的并发写安全,避免其他操作读到了修改变量过程中的脏数据。这些操作方法的定义与上面介绍的那些操作的方法类似,就不再演示怎么使用这些方法了。值得一提的是如果你想要并发安全的设置一个结构体的多个字段,除了把结构体转换为指针,通过
StorePointer
设置外,还可以使用atomic
包后来引入的atomic.Value
,它在底层为我们完成了从具体指针类型到unsafe.Pointer
之间的转换。有了
atomic.Value
后,它使得我们可以不依赖于不保证兼容性的unsafe.Pointer
类型,同时又能将任意数据类型的读写操作封装成原子性操作(中间状态对外不可见)。atomic.Value
类型对外暴露了两个方法:v.Store(c)
- 写操作,将原始的变量c
存放到一个atomic.Value
类型的v
里。c := v.Load()
- 读操作,从线程安全的v
中读取上一步存放的内容。1.17 版本我看还增加了
Swap
和CompareAndSwap
方法。下面是一个简单的例子演示
atomic.Value
的用法。完整源代码请访问:atomic示例二
你也可以试试,不用
atomic.Value
,直接给Rectange
类型的指针变量赋值,看看在并发条件下,两个字段的值是不是能跟预期的一样变成10和15。总结
原子操作由底层硬件支持,而锁则由操作系统的调度器实现。锁应当用来保护一段逻辑,对于一个变量更新的保护,原子操作通常会更有效率,并且更能利用计算机多核的优势,如果要更新的是一个复合对象,则应当使用
atomic.Value
封装好的实现。The text was updated successfully, but these errors were encountered: