脚本之家,脚本语言编程技术及教程分享平台!
分类导航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服务器之家 - 脚本之家 - Golang - goland -sync/atomic原子操作小结

goland -sync/atomic原子操作小结

2022-11-08 10:46Jeff的技术栈 Golang

这篇文章主要介绍了goland -sync/atomic原子操作,原子操作能够保证执行期间是连续且不会被中断(变量不会被其他修改,mutex可能存在被其他修改的情况),本文给大家介绍的非常详细,需要的朋友参考下

1.go已经提供了锁,为什么还需要atomic原子操作?

1.加锁代价比较高,耗时多,需要上下文切换。加锁解锁在代码层实现,而代码是运行在用户态空间中,对底层进行操作时需要从用户态空间切换到内核空间,再由内核操作底层资源。耗时多

2.原子操作在用户态可以完成,性能比互斥锁高。原子操作在cpu层面支持的,cpu可以直接操作底层资源

3.原子操作需求步骤简单,无需加锁解锁步骤

2.atomic原子操作为什么比mutex快?

1.原子操作快,是因为依赖于cpu指令,而不是依赖外部锁。不会额外的上下文切换
2.原子操作能够保证执行期间是连续且不会被中断(变量不会被其他修改,mutex可能存在被其他修改的情况)

3.CAS

CAS是cpu硬件同步原语,是Compare And Swap的缩写(比较并交换),原子操作中CAS,再sync/atomic包中,全部以ComparAndSwap开头的函数名都是CAS操作
   go中CAS操作,是借用CPU提供的原子性指令来实现。CAS操作修改共享变量时,不需要对共享变量加锁,而是通过类似乐观锁的方式进行检查,本质还是不断的占用CPU资源换取加锁带来的开销(如上下文切换时间开销)。

原子操作优势:
   可以在不形成临界区和创建互斥量的情况下完成并发安全的值替换操作。这可以大大的减少同步对程序性能的损耗。

原子操作劣势:
   在被操作值被频繁的变更的情况下,CAS操作并不那么容易成功。因为需要对ild值进行匹配,只有匹配成功了才进行下一步的修改。

当前atmomic包有以下几种原子操作:
   Add,ComparAndSwap,Load,Store,Swap

4.互斥锁与原子操作区别

互斥锁目的:互斥锁是用来保护一段逻辑的,保证并发安全。(比如操作数据库保护)
原子操作目的:原子操作作用于一个变量的更新保护,保证并发安全(比如操作数据库不能原子操作)

mutex底层实现:mutex由操作系统的调度器实现
原子操作底层实现:由底层硬件指令直接提供支持,这些指令在执行过程中不允许中断,因此原子操作可以在无锁的情况下保证并发安全,性能随cpu的数量增多而线性扩展。

5.原子操作方法

5.1 atomic.AddInt32--增减

增减,操作方法的命名方式为AddXXX,保证对操作数进行原子的增减,支持的类型为int32、int64、uint32、uint64、uintptr,使用时以AddXXX就是对应的操作方法。

?
1
2
3
4
5
6
7
8
9
10
11
12
//加
func demo() {
    var count int32 = 0
    atomic.AddInt32(&count, 10)
    fmt.Println(count) //10
}
//减
func demo() {
    var count int32 = 0
    atomic.AddInt32(&count, -10)
    fmt.Println(count) //-10
}

锁和原子操作对比:

?
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
//Mutex锁
func demo1() {
    sta := time.Now().Unix()
    count := 0
    mux := sync.Mutex{}
    wg := sync.WaitGroup{}
    for i := 0; i < 10000; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            for j := 0; j < 10000; j++ {
                mux.Lock()
                count++
                mux.Unlock()
            }
        }()
    }
    wg.Wait()
    fmt.Println(count) //100000000
    fmt.Println(time.Now().Unix() - sta) //10秒
}
 
//atomic原子操作:快2倍不止
func demo2() {
    sta := time.Now().Unix()
    wg := sync.WaitGroup{}
    var count int32 = 0
    for i := 0; i < 10000; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            for j := 0; j < 10000; j++ {
                atomic.AddInt32(&count, 1)
            }
        }()
    }
    wg.Wait()
    fmt.Println(count) //100000000
    fmt.Println(time.Now().Unix() - sta) //4秒
}

5.2 CAS-atomic.CompareAndSwapInt32--比较并替换

CompareAndSwap:比较并替换,类似乐观锁,先比较下old值与当前值是否一致,一致则把new的值替换
操作方法的命名方式为CompareAndSwapXXX

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//true
func demo3() {
    var count int32 = 0
    boo := atomic.CompareAndSwapInt32(&count, 0, 100)
    fmt.Println(count) //100
    fmt.Println(boo)   //true
}
 
 
//false
func demo3() {
    var count int32 = 0
    boo := atomic.CompareAndSwapInt32(&count, 10, 100)
    fmt.Println(count) //0
    fmt.Println(boo) //false
}

5.3 atomic.StoreInt32--写操作

?
1
2
3
4
5
func demo3() {
    var count int32 = 0
    atomic.StoreInt32(&count, 666)
    fmt.Println(count) //666
}

5.4 atomic.LoadInt32--读操作

?
1
2
3
4
5
6
7
func demo3() {
    var count int32 = 0
    atomic.StoreInt32(&count, 666)
 
    val := atomic.LoadInt32(&count)
    fmt.Println(val) //666
}

5.5 atomic.SwapInt32--直接交换

?
1
2
3
4
5
6
7
8
atomic.SwapInt32:直接交换,并返回交换前的值
 
func demo3() {
    var count int32 = 0
    old := atomic.SwapInt32(&count, 100)
    fmt.Println(old)   //0
    fmt.Println(count) //100
}

到此这篇关于goland-sync/atomic原子操作的文章就介绍到这了,更多相关goland sync/atomic原子操作内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://www.cnblogs.com/guyouyin123/p/16565192.html

延伸 · 阅读

精彩推荐
  • GolangGo语言中你所不知道的位操作用法

    Go语言中你所不知道的位操作用法

    位运算可能在平常的编程中使用的并不多,但涉及到底层优化,一些算法及源码可能会经常遇见。下面这篇文章主要给大家介绍了关于Go语言中你所不知道...

    python修行路2102020-05-12
  • Golanggo-micro集成RabbitMQ实战和原理详解

    go-micro集成RabbitMQ实战和原理详解

    本文主要介绍go-micro使用RabbitMQ收发数据的方法和原理,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    波斯马5392022-10-07
  • GolangGo语言获取文件的名称、前缀、后缀

    Go语言获取文件的名称、前缀、后缀

    这篇文章主要介绍了Go语言获取文件的名称、前缀、后缀,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    大川搬砖4662021-06-22
  • GolangGo并发调用的超时处理的方法

    Go并发调用的超时处理的方法

    这篇文章主要介绍了Go并发调用的超时处理的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧 ...

    cookedsteak6572020-05-22
  • Golanggo语言通过反射获取和设置结构体字段值的方法

    go语言通过反射获取和设置结构体字段值的方法

    这篇文章主要介绍了go语言通过反射获取和设置结构体字段值的方法,实例分析了Go语言反射的使用技巧,需要的朋友可以参考下 ...

    脚本之家6712020-04-22
  • Golanggolang中接口对象的转型两种方式

    golang中接口对象的转型两种方式

    这篇文章主要介绍了golang中接口对象的转型方式,大家都知道接口对象的转型有两种方式,文中通过示例代码给大家介绍的非常详细,需要的朋友可以参考下...

    专职5402021-11-21
  • GolangGo语言编程中字符串切割方法小结

    Go语言编程中字符串切割方法小结

    这篇文章主要介绍了Go语言编程中字符串切割方法小结,所整理的方法都来自字符串相关的strings包,需要的朋友可以参考下 ...

    脚本之家3132020-04-27
  • Golanggo语言用八百行代码实现一个JSON解析器

    go语言用八百行代码实现一个JSON解析器

    这篇文章主要为大家介绍了go语言用八百行代码实现一个JSON解析器实例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升...

    crossoverJie5672022-07-14