0%

Go语言原子性atomic

知法、懂法、守法、用法,做新时代法治社会好青年。

一、基础

      一个或者多个操作在CPU执行的过程中不被中断的特性,称为原子性(atomicity)。这些操作对外表现成一个不可分割的整体,他们要么都执行,要么都不执行,外界不会看到他们只执行到一半的状态。

      Go语言通过内置包sync/atomic提供了对原子操作的支持,其提供的原子操作有以下几大类:

  • 增减:操作方法的命名方式为AddXXXType,保证对操作数进行原子的增减,支持的类型为int32、int64、uint32、uint64、uintptr。
  • 载入:保证了读取到操作数前没有其他任务对它进行变更,操作方法的命名方式为LoadXXXType,支持的类型除了基础类型外还支持Pointer,也就是支持载入任何类型的指针。
  • 存储:方法名以Store开头,支持的类型跟载入操作支持的那些一样。
  • 比较并交换,也就是CAS(Compare And Swap),像Go的很多并发原语实现就是依赖的CAS操作,同样是支持上面列的那些类型。
  • 交换:不比较直接交换,这个操作很少会用。

二、使用

  1. 基本用法
    • 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
package main

import (
"fmt"
"sync"
"sync/atomic"
)

func decStock(stock *int32,wg *sync.WaitGroup) {
atomic.AddInt32(stock, -1)
//*stock--
defer wg.Done()
}
func main() {
var num int32 = 10
var wg sync.WaitGroup
wg.Add(10)
for i :=0; i<10; i++ {
go decStock(&num, &wg)
}
wg.Wait()
fmt.Println(num)
}
  • go run –race main.go
  1. 使用锁和原子操作对比

    • 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
    48
     package 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

三、参考

  1. 参考一