Featured image of post Go语言Channel详解

Go语言Channel详解

Go语言Channel个人学习笔记

源码解读版本1.22

简介

go语言提供的并发模型是通信顺序进程(csp)。goroutine和channel分别对应csp中的实体和传递信息的媒介,goroutine之间通过channel传递数据。

目前channel的收发操作都遵循 先进先出 的设计。channel的内部表示的hchan结构体,结构体中包含了用于保护成员变量的 互斥锁。所以说,在某种程度上,channel是一个 有锁队列

关键源码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
type hchan struct {
    qcount   uint
    dataqsiz uint
    buf      unsafe.Pointer
    elemsize uint16
    closed   uint32
    elemtype *_type
    sendx    uint
    recvx    uint
    recvq    waitq
    sendq    waitq

    lock mutex
}

相关字段含义:

  1. qcount表示channel中的元素个数;

  2. dataqsiz表示channel中循环队列的长度;

  3. buf表示channel的缓冲区的数据指针;

  4. sendx和recvx分别表示发送和接收操作处理到的位置;

  5. sendq和recvq分别表示由于缓冲区空间不足而阻塞的goroutine列表;

goroutine使用waitq结构体表示,他是一个双向链表。

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

链表中每个元素都是sudog结构,他用来表示等待列表中的一个goroutine。

channel的创建

channel的创建都会使用 make 关键字,他会对缓冲区大小参数进行检查,如果没有,他会默认为0。然后根据传入的缓冲区的大小调用makechan或makechan64函数,64的话是用来处理缓冲区大小大于232的情况,这种情况的话很少见。

在makechan中会分三种情况:

  1. 如果channel中不存在缓冲区,那就为hchan分配一块内存空间。

  2. 如果channel中存储的不是指针类型,会为当前channel和底层数组分配一块连续的内存空间。

  3. 默认情况下会为hchan和缓冲区单独分配空间。

无缓冲的channel

一个基于无缓存Channels的发送操作将导致发送者goroutine阻塞,直到另一个goroutine在相同的Channel上执行接收操作,当发送的值通过Channel成功传输之后,两个goroutine可以继续执行后面的语句。反之,如果接收操作先发生,那么接收者goroutine也将阻塞,直到有另一个goroutine在相同的Channel上执行发送操作。

有缓冲的channel

向缓存Channel的发送操作就是向内部缓存队列的尾部插入元素,接收操作则是从队列的头部删除元素。如果内部缓存队列是满的,那么发送操作将阻塞直到因另一个goroutine执行接收操作而释放了新的队列空间。相反,如果channel是空的,接收操作将阻塞直到有另一个goroutine执行发送操作而向队列插入元素。

关闭的channel

对于已经关闭的channel,如果通道内已经没有数据了,可以不限次数的读取,但是读到的是该数据类型的零值 如果通道内还有数据,那么仍然可以读到之前存储的数据,ok会返回true,表示通道内有数据,当通道内的数据读完时,也会返回零值。

但是如果向已经关闭的channel写入数据会panic。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计