第一句子大全,网罗天下好句子,好文章尽在本站!

深度好文:Go语言channel剖析

时间:2023-11-24 07:56:02

相关推荐

深度好文:Go语言channel剖析

Go内建channel实现了go协程之间数据的读写相关操作。

并发在Go当中不仅仅是语法。它是一种设计模式。该模式提供了在处理常见并发问题的解决方案。因为并发需要同步 。Go并发是源自CSP模型,通过channel来实现协程的同步。Go并发哲学是:不通过共享内存来通信,而是通过通信来共享内存。

但Go相信你会做正确的事。因此下文将揭示Go这一哲学,channel是如何使用队列来实现的。

如何创建channel

func goRoutineA(a <-chan int) {

val := <-a

fmt.Println("goRoutineA received the data", val)

}

func main() {

ch := make(chan int)

go goRoutineA(ch)

time.Sleep(time.Second * 1)

}

因此,在Go中让受读写channel阻塞的Goroutine再次进入可执行状态是channel的责任,当channel收到货发送了数据就会触发goroutine状态的转变。

Channel结构

在Go当中,channel结构体是goroutine之间传递消息的基础。因此当我们创建了channel之后,其内部结构是怎样的呢?

ch := make(chan int, 3)

带缓冲的channel结构

看着不错,但这些代表啥意思呢?channel是从哪创建来的呢?我们在深入之前先了解一些重要的结构体。

hchan结构体

当我们写make(chan int, 2),channel就根据hchan结构体来创建的,包含如下字段:

hchan和waitq结构体

让我们对在channel结构中遇到的几个字段进行描述。 dataqsize:是缓存大小,就是make(chan T, N),N的大小 elemsize:是channel单个元素大小 buf:是循环队列,channel数据存放的地方,仅带缓存channel使用 close:代表当前channel是否已经关闭状态。当channel刚创建这个值是0,代表channel是开的;当值为1的时,channel关闭。 sendx和recvx:是环形缓存的状态值,表示当前缓存索引,根据这两值可以知道缓存数据发送和接收的索引位置。 sendq和recvq:等待队列,分别用于存储阻塞的发送数据的goroutine和读取数据的goroutine。 lock:锁定channel每个读和写操作,发送和接收必须是互斥操作。

那sudog代表啥? Sudog代表goroutine。

sudog关键字段

我们在一步步的缕下channel结构。

以上代码中,在22行之前channel的结构是怎样的?

注意上面高亮显示的第47和48行。记住上面的recvq的作用:

recvq用于存储受读channel阻塞的goroutine

在上面代码中22行之前,有两个goroutine(goroutineA和goroutineB)需要从channel中读取数据。

因为在22行之前,channel中并没有数据可读,因此两个goroutine都因读取数据受阻塞,并以sudog结构保存在recvq中。

sudog表示goroutine

recvq和sendq本质是链表,如下图所示:

recvq结构

这些结构体很重要。我们看下当把数据写入channel会发生什么。

发送操作:c <- x

channel发送数据的底层类型

1、发送数据到nil channel

如果我们在nil通道上发送数据,当前的goroutine将暂停它的操作。 2、向关闭的channel发送数据

如果试图向关闭的channel发送数据,goroutine会panic,协程退出。 3、存在goroutine在通道上被阻塞:数据直接发送到goroutine。

这是recvq结构体起重要作用的地方。如果在recvq中有goroutine在等待,当前写入channel的数据会直接传给对应阻塞的goroutine。发送函数的实现。

注意以上代码的396行goready(gp, skip+1),在等待数据受阻塞的goroutine通过goready函数重新变为可运行状态,go调度器会再次运行该goroutine。 4、带缓存的channel如果hchan.buf还有空间的话,发送的数据会存在buffer中

chanbuf(c, i)函数访问对应的内存区域。决定hchan.buf是否还有空间是通过比较qcount和dataqsiz来实现的。 5、hchan.buf满了

在当前栈中创建一个goroutine对象。acquireSudog函数将当前goroutine变为park状态,将goroutine添加到channel的sendq中。

发送操作总结

1、锁定整个channel结构 2、决定写。从等待队列recvq中取出一个等待的goroutine,并将数据直接写入等待的goroutine。 3、如果recvq是空的,考虑buffer是否已满。buffer有空间就通过typedmemmove将数据拷贝到缓存。拷贝的实现方式:memmove()函数内存拷贝。 4、如果buffer满了的话,写入的值会保存在当前执行的goroutine中,并将当前goroutine存在sendq队列并从runtime中挂起。 第4点很有意思,如果缓冲区已满,则要写入的元素将保存在当前正在执行的goroutine的结构中。这就是为什么不带缓存的channel称为unbuffered,即使hchan结构体中包含buf字段。因为不带缓存的channel如果没有接收者的话,发送数据到channel,数据会存放在sudog结构的elem字段。

让我给你一个例子来更详细地阐明第四点。假设我们有以下代码。

在第10行,chan c2的运行时结构是什么?

可以看到以上代码中buf中并没有保存整数2,会保存在sudog结构中。由于goroutineA试图通过c2通道发送数据,当时并没有接收者,因此goroutineA将被添加到c2通道的sendq中并挂起。我们可以查看sendq的运行时结构来验证。

现在,我们已经对通道上的发送操作有了概述,前面的示例代码第22行,如果一旦我们将一个值发送到channel,会发生什么。

ch <- 3

由于该channel的recvq有goroutine处于等待状态,它将等待队列中第一个sudog取出,并将数据直接传给该goroutine。

记住channel中所有的值转移都是值拷贝

以上程序的输出是什么?记住,通道对值的副本进行操作。 在我们的例子中,channel会将g处的值复制到它的缓冲区中。 输出:

&{Ankur 25}

modifyUser Received Value &{Ankur Anand 100}

printUser goRoutine called &{Ankur 25}

&{Anand 100}

写入数据操作<-

和发送数据非常类似:

Select

多个channel的复用

1、操作是互斥的,因此需要在select case中获取所有相关通道上的锁,这是通过按Hchan地址对case进行排序来获得锁定顺序,这样就不会同时锁定所有相关通道上。

sellock(scases, lockorder)

scases数据中的每个scase都是一个结构体,包含当前case操作类型和对应的channel操作。

kind,是当前case的操作类型,可能是:CaseRecv,CaseSend和CaseDefault。 2、通过计算投票顺序,将所有的case都重新洗牌,提供一种伪随机保证。然后根据轮询顺序依次遍历所有case,查看是否有任何case已经准备好进行通信。这个轮询顺序使得选择操作不必遵循程序中声明的顺序。

计算选择顺序

select中的case

3、只要有一个不阻塞的channel操作,select语句就可以返回,如果选择的通道已经准备好了,甚至不需要查看所有的channel。 4、如果当前没有channel响应,并且没有default语句,则当前goroutine必须根据当前case,在等待队列中挂起。

sg.isSelect表示当前goroutine参与select语法声明。 5、在select语句中读取、发送和关闭操作和通常channel的读取、发送和关闭是类似的。

总结

channel在go中是一种非常有意思而且重要的机制。要想很好的使用channel就必须理解其工作原理。本文介绍了go中channel的基本工作原理。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。
显示评论内容(3)
  1. 东莞娱乐2024-02-10 21:17东莞娱乐[海南省网友]111.67.194.239
    对于想要深入了解Go语言channel的人来说,这篇文章绝对是必读之作。
    顶7踩0
  2. 一心一意︶2024-01-15 16:50一心一意︶[山东省网友]203.0.18.120
    深入浅出,让人能够更好地理解Go语言中的channel。
    顶1踩0
  3. SMIS2023-12-20 12:23SMIS[西藏网友]43.224.74.40
    很有意思的文章,解释了Go语言channel的原理和用法。
    顶1踩0
相关阅读
深度剖析双子座爱情观 六句话让你后悔对双子座放手!神准!

深度剖析双子座爱情观 六句话让你后悔对双子座放手!神准!

双子爱上一个人之后就会患得患失双子座的人如果陷入了爱情,就会收敛他们那颗放荡的心,甚至在自己喜欢的人面前变得有些神经质,每天患得患失的,害怕失

2023-11-04

生活教会我的31件事:句句透彻 深度剖析现实

生活教会我的31件事:句句透彻 深度剖析现实

2 当爱情慢慢淡去,两个人的关系是靠善良来维系

2024-01-19

从高考英语文章来源 剖析高考英语句子成分——训练英语精读理解

从高考英语文章来源 剖析高考英语句子成分——训练英语精读理解

所以,加强这些英文载体里的句子理解,不但能综合复习词汇和语法,还能够让学习者练习英语学习中最重要,但是缺失的能力

2022-11-17

想了解自然语言处理技术的专利发展情况?看这里

想了解自然语言处理技术的专利发展情况?看这里

最近几年,科技巨头和创业公司相继对自然语言处理技术进行商业化探索,不过,除了语音和机器翻译领域之外,自然语言处理技术在很多方面的进展并不大,自

2023-08-11