知法、懂法、守法、用法,做新时代法治社会好青年。
一、基础
一个或者多个操作在CPU执行的过程中不被中断的特性,称为原子性(atomicity)。这些操作对外表现成一个不可分割的整体,他们要么都执行,要么都不执行,外界不会看到他们只执行到一半的状态。
Go语言通过内置包sync/atomic
提供了对原子操作的支持,其提供的原子操作有以下几大类:
- 增减:操作方法的命名方式为AddXXXType,保证对操作数进行原子的增减,支持的类型为int32、int64、uint32、uint64、uintptr。
- 载入:保证了读取到操作数前没有其他任务对它进行变更,操作方法的命名方式为LoadXXXType,支持的类型除了基础类型外还支持Pointer,也就是支持载入任何类型的指针。
- 存储:方法名以Store开头,支持的类型跟载入操作支持的那些一样。
- 比较并交换,也就是CAS(Compare And Swap),像Go的很多并发原语实现就是依赖的CAS操作,同样是支持上面列的那些类型。
- 交换:不比较直接交换,这个操作很少会用。
二、使用
- 基本用法
- main.go
1 | package main |
- go run –race main.go
使用锁和原子操作对比
- main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48package main
import (
"fmt"
"sync"
"sync/atomic"
"time"
)
func main() {
MutexAdd()
AtomicAdd()
}
func MutexAdd() {
var a int32 = 0
var wg sync.WaitGroup
var mu sync.Mutex
start := time.Now()
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
mu.Lock()
a += 1
mu.Unlock()
}()
}
wg.Wait()
timeSpends := time.Since(start)
fmt.Printf("use mutex a is %d, spend time: %v\n", a, timeSpends)
}
func AtomicAdd() {
var a int32 = 0
var wg sync.WaitGroup
start := time.Now()
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
atomic.AddInt32(&a, 1)
}()
}
wg.Wait()
timeSpends := time.Since(start)
fmt.Printf("use atomic a is %d, spend time: %v\n", atomic.LoadInt32(&a), timeSpends)
}- go run main.go