Golang sync/atomic包——原子操作

1、概述

1.1 基本概念

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

原子操作:进行过程中不能被中断的操作,原子操作由底层硬件支持,而锁则是由操作系统提供的API实现,若实现相同的功能,前者通常会更有效率

Golang 中的原子操作:sync/atomic包

能够进行原子操作的类型:int32, int64, uint32, uint64, uintptr, unsafe.Pointer

五种操作函数:增或减、比较并交换、载入、存储、交换

原子操作比锁更为高效。

1.2 原子操作 vs 锁

  • 加锁比较耗时,需要上下文切换。即使是goroutine也需要上下文切换
  • 只针对基本类型,可使用原子操作保证线程安全
  • 原子操作在用户态完成,性能比互斥锁要高
  • 原子操作步骤简单,不需要加锁-操作-解锁

1.3 五种操作

  • 增或减 (Add)
  • 比较并交换 (CAS, Compare & Swap)
  • 载入 (Load)
  • 存储 (Store)
  • 交换 (Swap)

1.4 最小案例

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 (
    "sync"
    "fmt"
)
 
var count int
 
func add(wg *sync.WaitGroup) {
    defer wg.Done()
    count++
}
 
func main() {
    wg := sync.WaitGroup{}
    wg.Add(1000)
    for i := 0; i < 1000; i++ {
        go add(&wg)
    }
    wg.Wait()
    fmt.Println(count)
}

count不会等于1000,因为count++这一步实际是三个操作:

  • 从内存读取count
  • CPU更新count = count + 1
  • 写入count到内存

因此就会出现多个goroutine读取到相同的数值,然后更新同样的数值到内存,导致最终结果比预期少。

2、sync/atomic包使用

Go语言提供的原子操作都是非入侵式的,由标准库中sync/aotomic中的众多函数代表

atomic包中支持六种类型

  • int32
  • uint32
  • int64
  • uint64
  • uintptr
  • unsafe.Pointer

对于每一种类型,提供了五类原子操作:

  • LoadXXX(addr): 原子性的获取*addr的值,等价于:
    1
    return *addr
  • StoreXXX(addr, val): 原子性的将val的值保存到*addr,等价于:
    1
    addr = val
  • AddXXX(addr, delta): 原子性的将delta的值添加到*addr并返回新值(unsafe.Pointer不支持),等价于:
    1
    2
    *addr += delta
    return *addr
  • SwapXXX(addr, new) old: 原子性的将new的值保存到*addr并返回旧值,等价于:
    1
    2
    3
    old = *addr
    *addr = new
    return old
  • CompareAndSwapXXX(addr, old, new) bool: 原子性的比较*addrold,如果相同则将new赋值给*addr并返回true,等价于:
    1
    2
    3
    4
    5
    if *addr == old {
        *addr = new
        return true
    }
    return false
Go sync/atomic api

因此第一部分的案例可以修改如下,即可通过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 修改方式1
func add(wg *sync.WaitGroup) {
    defer wg.Done()
    atomic.AddInt32(&count, 1)
}
 
// 修改方式2
func add(wg *sync.WaitGroup) {
    defer wg.Done()
    for {
        if atomic.CompareAndSwapInt32(&count, count, count+1) {
            break
        }
    }
}

参考:https://juejin.cn/post/6844904053042839560

参考:https://blog.csdn.net/elihe2011/article/details/109157797

参考:https://segmentfault.com/a/1190000040545690

posted @   人艰不拆_zmc  阅读(773)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
历史上的今天:
2018-01-18 jconsole监控远程 spring boot程序
2018-01-18 Dokcerfile部署webpy,安装imagehash库并运行py脚本获取图片dhash值
2017-01-18 centos6.8安装Discuz!X3.1(PHP论坛)
2015-01-18 POJ1061:青蛙的约会+POJ2115C Looooops+UVA10673Play with Floor and Ceil(扩展欧几里得)
点击右上角即可分享
微信分享提示