栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Go语言

go语言数据结构之管道(channel)实现原理

Go语言 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

go语言数据结构之管道(channel)实现原理

     

        在go语言中channel是核心的数据类型,主要用来解决协程的同步以及协程之间数据共享(数据传递)的问题,本篇主要从底层实现来分析在go语言中channel是如何实现的。

      首先我们看一下channel的数据结构,在源码包中src/runtime/chan.go中定义了channel的数据结构:       

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 mutex
}

 从数据结构可以看出channel由队列、类型信息、协程等队列组成。

环形队列:channel内部实现了一个环形队列作为其缓冲区,队列的长度是在创建channel时指定的,下图展示了一个可缓存8个元素的channel。

dataqsiz指示了队列长度为8,即可缓存8个元素;

buf指向队列的内存;

qcount表示队列中还有两个元素;

sendx表示后续写入的数据存储的位置,取值为[0, 8);

recvx表示从该位置读取数据,值为[0, 8);

其中,sendx表示队尾,即数据写入的位置;recvx表示队首,代表数据读取的位置。

等待队列:从channel读取数据时,如果channel缓冲区为空或没有缓冲区,则当前读协程会被阻塞,并被加入recvq队列。向channel写入数据时,如果缓冲区已满或没有缓冲区,则当前写协程会被阻塞,并被加入sendq队列。

下图展示了一个没有缓冲区的管道,有三个协程阻塞等待读数据。

 处于等待队列中的协程会在其他协程操作channel时被唤醒:

        因读阻塞的协程会被向管道写入数据的协程唤醒;

        因写阻塞的协程会被从管道读取数据的协程唤醒。

一般情况下recvq和sendq至少有一个为空。只有一个例外,那就时同一个协程使用select语句向管道一边写入数据,一边读取数据,此时协程会分别位于两个等待队列中。

一个channel只能传递一种类型的值,类型信息存储在hchan数据结构中。

elemtype代表类型,用于在数据传递过程中赋值;

elemsize代表类型大小,用于在buf中定位元素的位置。

channel的操作

1)创建channel

创建管道的过程实际上是初始化hchan结构,其中类型信息和缓冲区长度由内置函数make()指定,buf的大小则由元素大小和缓冲区长度共同决定。

ch := make(chan int, 10)

2)向channel中写入数据的过程如下:

        如果缓冲区中有空余位置,则将数据直接写入缓冲区。

        如果缓冲区中没有空余位置,则将当前写协程加入sendq队列,进入睡眠并等待被读唤                    醒。

操作符“<-"表示数据流向,channel在左表示写入数据,在右表示从channel读数据。

ch <- 1 //数据流入channel

协程写入channel时造成阻塞的条件有:

        channel无缓冲区;

        channel的缓冲区已满;

        channel的值为nil;

3)从channel读数据的过程如下:

        如果缓冲区中有数据,则从缓冲区中取出数据,结束读取过程。

        如果缓冲区没有数据,则将读协程加入recvq队列,进入睡眠并等待被写协程唤醒。

d := <- ch  //数据流出channel

协程读取管道时,阻塞的条件有:

        channel无缓冲区;

        channel的缓冲区中无数据;

        channel的值为nil。

4)关闭channel:

        关闭channel时会把recvq中的协程全部唤醒,这些协程获取的数据都为nil。同时会把sendq队列中的协程全部唤醒,但这些协程会触发panic。

        另外,以下操作也会触发panic:

                关闭值为nil的channel;

                关闭已经被关闭的channel;

                向已经关闭的channel写入数据。

close(chan)

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/993599.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号