golang atomic看这篇就够啦

atomic

atomic是go提供的一个执行原子操作的包,虽然提供了这个包,但是go官方并不是很推荐使用;
除了做一些低级的应用程序外,go更推荐使用通道和sync来处理;

PS: go中的sync.Mutex底层都是通过atomic来实现的;这么看atomic确实比较低级。

虽然它比较底层,但是我还是有必要了解使用它。

1. 什么是原子操作呢?

可以这么简单的理解,在程序执行某一个操作的时候,在计算机底层其实是分了多个步骤去处理它;
有多个步骤处理,那么就意味着有中间状态(操作中、没操作完的状态);

而原子操作,它是一个不可分割的整体,没有中间状态,要么成功了、要么失败了。,它通过底层的cpu操作去实现。

这样有什么好处,在多goroutine并发操作的同一个数据的时候,可以保护数据的一致性。

2. atomic 概述

在golang中,atomic主要提供了下面几种操作:

  1. Store - 存一个值
  2. Load - 获取一个值
  3. Swap - 更新一个值(返回旧值)
  4. Add - 加值
  5. CompareAndSwap - 比较然后更新值(如果值还是原来的值,则更新)

另外主要是对下面的类型实现原子操作:

  • int32
  • int64
  • uint32
  • unint64
  • uintptr

函数格式为操作 + 类型,比如:
对于int32,会有下列函数:

  • atomic.AddInt32()
  • atomic.StoreInt32()
  • atomic.LoadInt32()
  • atomic.SwapInt32()
  • atomic.CompareAndSwapInt32(),其它类型同理。

虽然go提供了函数操作,但是go鼓励我们采用,方法调用(更人性化、不臃肿).
比如atomic.AddInt32()对应的方法为Add()非常简洁哦~

3. 函数调用方式

需要注意的是:atomic操作的都是地址
由于每种类型使用方式基本差不多,这里拿int32举例演示

package main

import (
    "fmt"
    "sync/atomic"
)

func main() {
    var a int32

    // 将1 存给a变量
    atomic.StoreInt32(&a, 1)

    fmt.Println(a)
    fmt.Println(atomic.LoadInt32(&a)) // 读取a地址的值

    // 更新值
    oldValue := atomic.SwapInt32(&a, 2)
    fmt.Println("新值:", a, "旧值:", oldValue)

    // 添加值
    atomic.AddInt32(&a, 2) // 加2
    fmt.Println("增加后值:", a)
    atomic.AddInt32(&a, -1) // 减1
    fmt.Println("减少后值:", a)

    // 比较后更新 返回是否更新成功
    swapped := atomic.CompareAndSwapInt32(&a, 3, 6) // 如果旧值是3 则更新为6
    fmt.Println("第一次比较更新是否成功:", swapped, "当前值为:", a)

    swapped = atomic.CompareAndSwapInt32(&a, 4, 3) // 如果旧值是4 则更新为3
    fmt.Println("第二次比较更新是否成功: ", swapped, "当前值为:", a)
}

// 1
// 1
// 新值: 2 旧值: 1
// 增加后值: 4
// 减少后值: 3
// 第一次比较更新是否成功: true 当前值为: 6
// 第二次比较更新是否成功:  false 当前值为: 6

4. 方法调用方式

go推荐使用这种,简洁。

直接将函数调用方式改成方法调用方式,如下:

package main

import (
    "fmt"
    "sync/atomic"
)

func main() {
    // 使用atomic内置的类型(结构体)
    // 注意这个时候取值 要用a.Load()方式
    var a atomic.Int32

    // 将1 存给a变量
    a.Store(1)

    fmt.Println(a.Load()) // 读取a地址的值

    // 更新值
    oldValue := a.Swap(2)
    fmt.Println("新值:", a.Load(), "旧值:", oldValue)

    // 添加值
    a.Add(2) // 加2
    fmt.Println("增加后值:", a.Load())
    a.Add(-1) // 减1
    fmt.Println("减少后值:", a.Load())

    // 比较后更新 返回是否更新成功
    swapped := a.CompareAndSwap(3, 6) // 如果旧值是3 则更新为6
    fmt.Println("第一次比较更新是否成功:", swapped, "当前值为:", a.Load())

    swapped = a.CompareAndSwap(4, 3) // 如果旧值是4 则更新为3
    fmt.Println("第二次比较更新是否成功: ", swapped, "当前值为:", a.Load())
}

看看是不是简介了很多。

5. 无符号类型做减法

对于有符号类型,Add时候直接给一个负数是没有问题的;但是对于无符号类型比如(uint32,uint64)不能直接给负数——无符号没有负数,这个时候需要转换下,

怎么转换下呢?
在Add的时候,加一个按位取反,然后+1的数

下面看代码:

package main

import (
    "fmt"
    "sync/atomic"
)

func main() {
    // 无符号类型整数
    var a atomic.Uint32

    // 将1 存给a变量
    a.Store(10)

    fmt.Println(a.Load()) // 读取a地址的值

    // 添加值
    a.Add(2)                       // 加2
    fmt.Println("增加后值:", a.Load()) // 加完后当前为 12
    a.Add(^uint32(3) + 1)          // 减3 按位取反 然后加1

    fmt.Println("第一次减少后值:", a.Load())

    // 减少1
    a.Add(^uint32(1) + 1)
    fmt.Println("第二次减少后值: ", a.Load())

    // 再次减少1
    a.Add(^uint32(0)) // ^uint32(0) 等价于 ^uint32(1) + 1
    fmt.Println("第三次减少后值: ", a.Load())
}

// 10
// 增加后值: 12
// 第一次减少后值: 9
// 第二次减少后值:  8
// 第三次减少后值:  7

6. Value类型(任意类型)

前面的操作都是针对某个类型的操作,这个类型可以对任意类型操作
PS: 这种类型,没有Add方式

package main

import (
    "fmt"
    "sync/atomic"
)

type Person struct {
    name string
    age  int8
}

func main() {
    // 任意类型
    var a atomic.Value

    p := Person{
        name: "张三",
        age:  18,
    }
    // 将p 结构体存入
    a.Store(p)

    fmt.Printf("取出值为:%#v\n", a.Load()) // 读取a地址的值
}

// 取出值为:main.Person{name:"张三", age:18}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,919评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,567评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,316评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,294评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,318评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,245评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,120评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,964评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,376评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,592评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,764评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,460评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,070评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,697评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,846评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,819评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,665评论 2 354

推荐阅读更多精彩内容