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

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

服务器之家 - 脚本之家 - Golang - Go保证并发安全底层实现详解

Go保证并发安全底层实现详解

2022-09-02 15:12Sundar84034 Golang

这篇文章主要为大家介绍了Go保证并发安全底层实现详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

引言

上一部分主要写了锁,本篇主要介绍Channel

channel是Go中非常重要的一个数据类型,它和goroutine紧密相连,是Go的CSP并发模型的重要体现。

CSP

  • CSP 是通信顺序进程(Communicating Sequential Process)的简称,是一种并发编程模型。
  • 简单来说,CSP模型由并发的实体所组成,实体之间通过发送消息进行通信,而发送消息使用的就是通道,即channel。
  • GO实现了CSP部分理论,goroutine对应CSP中的并发执行的实体,channel对应CSP中的channel。

不要通过共享内存来通信,而应该通过通信来共享内存

Channel的基本使用

?
1
2
3
4
5
6
7
8
9
10
package main
import "fmt"
func main() {
    c := make(chan int)
    go func() {
        c <- 1 // 向channel发送数据
    }()
    x := <-c // 从channel中接收数据
    fmt.Println(x)
}

1、通过make(chan int)创建一个int channel(可以在channel初始化时指定缓冲区的大小,例如make(chan int,2),不指定则默认为0)

2、在一个goroutine中,通过c<-1将数据发送到channel中,<-可以理解为数据的流动方向。

3、在主goroutine中通过x := <-c接收channel中的数据,并赋值给x。

channel如何保证并发安全

既然goroutin和channel分别对应csp中的实体和媒介,goroutin之间都是通过chennel来传递数据,那么是如何保证并发安全的呢?

通过阅读源码可以发现,channel内部是使用Mutext互斥锁来保证的( 之前也有人提出CAS无锁Channel的实现,但因为无锁Channel在多核测试中的表现和没有满足FIFO的特性等原因,该提案目前是搁浅状态)关于无锁channel的讨论

channel的底层实现

channel的核心源码位于runtime包的chan.go中。

hchan 是 channel 在 golang 中的内部实现

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
type hchan struct {
    qcount uint // total data in the queue
    dataqsiz uint // size of the circular queue
    buf unsafe.Pointer // points to an array of dataqsiz elements
    elemsize uint16
    closed uint32
    elemtype *_type // element type
    sendx uint // send index
    recvx uint // receive index
    recvq waitq // list of recv waiters
    sendq waitq // list of send waiters
    // lock protects all fields in hchan, as well as several
    // fields in sudogs blocked on this channel.
    //
    // Do not change another G's status while holding this lock
    // (in particular, do not ready a G), as this can deadlock
    // with stack shrinking.
    lock mutex
 }

hchan的所有属性大体可以分为3类

1、buffer相关属性,当channel中的缓冲区大小不为0时,buffer中存放了待接收的数据。

2、waitq相关属性,即recvq和sendq,可以理解为一个标准的FIFO队列,recvq是等待接收数据的goroutine,sendq是等待发送数据的goroutine。

3、其它,例如lock(互斥锁)、elemtype(元素类型)、closed(channel 是否关闭,== 0 代表未 closed)

hchan的所有行为,基本都是围绕bufferwaitq来实现的

waitq

?
1
2
3
4
type waitq struct {
 first *sudog
 last *sudog
 }

waitq是一个双向链表,里面保存了goroutine。

buffe

buffer使用 ring buffer(环形缓冲区)实现

在hchan中,可以看到 recvxsendx 两个属性,recvx即当前已发送的元素在队列当中的索引位置,sendx 即 当前已接收的元素在队列当中的索引位置。

从 recvx 到 sendx 之间的元素,表示已正常存放入 buffer 中的数据。

Lock

hchan中的lock就是一个互斥锁,channel在发送和接收数据前,都会先进行加锁,待逻辑完成后执行再解锁,来保证并发安全。

以上就是Go保证并发安全底层实现详解的详细内容,更多关于Go并发安全底层实现的资料请关注服务器之家其它相关文章!

原文链接:https://juejin.cn/post/7026230343581564959

延伸 · 阅读

精彩推荐
  • Golanggo语言睡眠排序算法实例分析

    go语言睡眠排序算法实例分析

    这篇文章主要介绍了go语言睡眠排序算法,实例分析了睡眠排序算法的原理与实现技巧,需要的朋友可以参考下 ...

    feiwen5842020-04-21
  • GolangGo Module:私有不合规库怎么解决引用问题

    Go Module:私有不合规库怎么解决引用问题

    实际项目中,使用 Go Module 难免会有一些自己的库要引用,这些库使用自建的 Git 服务管理,比如 GitLab 等。针对这样的情况,不少教程都是让设置 GOPRIVAT...

    polarisxu6842021-09-15
  • GolangGo语言利用time.After实现超时控制的方法详解

    Go语言利用time.After实现超时控制的方法详解

    最近在学习golang,所以下面这篇文章主要给大家介绍了关于Go语言利用time.After实现超时控制的相关资料,文中通过示例介绍的非常详细,需要的朋友可以参...

    格物2822020-05-18
  • GolangGo select 死锁的一个细节

    Go select 死锁的一个细节

    这篇文章主要给大家分享的是Go select 死锁的一个细节,文章先是对主题提出问题,然后展开内容,感兴趣的小伙伴可以借鉴一下,希望对你有所帮助...

    polarisxu11862021-11-19
  • GolangGo语言创建、初始化数组的常见方式汇总

    Go语言创建、初始化数组的常见方式汇总

    这篇文章主要介绍了Go语言创建、初始化数组的常见方式,实例汇总了Go语言操作数组的常见技巧,具有一定参考借鉴价值,需要的朋友可以参考下 ...

    脚本之家6022020-04-13
  • GolangGolang使用第三方包viper读取yaml配置信息操作

    Golang使用第三方包viper读取yaml配置信息操作

    这篇文章主要介绍了Golang使用第三方包viper读取yaml配置信息操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    mrtwenty18922021-03-05
  • GolangGo 中实现超时控制的方案

    Go 中实现超时控制的方案

    这篇文章主要介绍了Go 里的超时控制实现方案,本文给大家带来两种解决方案,第一种方案是 Time.After(d Duration),第二种方案是利用 context,go 的 context 功能...

    crossoverJie9102021-11-25
  • Golanggo zero微服务实战性能优化极致秒杀

    go zero微服务实战性能优化极致秒杀

    这篇文章主要为大家介绍了go-zero微服务实战性能优化极致秒杀功能实现,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加...

    kevinwan9032022-07-14