golang channel

channel分两种, 一种是带缓冲的channel一种是不带缓冲的channel

结构定义

type hchan struct {
	//channel队列里面总的数据量
	qcount   uint           // total data in the queue
	// 循环队列的容量,如果是非缓冲的channel就是0
	dataqsiz uint           // size of the circular queue
	// 缓冲队列,数组类型。
	buf      unsafe.Pointer // points to an array of dataqsiz elements
	// 元素占用字节的size
	elemsize uint16
	// 当前队列关闭标志位,非零表示关闭
	closed   uint32
	// 队列里面元素类型
	elemtype *_type // element type
	// 队列send索引
	sendx    uint   // send index
	// 队列索引
	recvx    uint   // receive index
	// 等待channel的G队列。
	recvq    waitq  // list of recv waiters
	// 向channel发送数据的G队列。
	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
}
  • 一个数组实现的FIFO,数组有两个下标索引(sendx, recvx)分别表示读写的索引,用于保存channel缓冲区数据
  • channel的sendq和recvq队列,队列里面都是持有goroutine的sudog元素,队列都是双链表实现的
  • channel的全局锁

buffered channel

golang channel_第1张图片
img

当前routine写数据

往buf里写数据

不同goroutine在channel上面进行读写时

写入channel

1. 获取lock
2. 入队
3. 释放lock

读出channel

1. 获取lock
2. 出队
3. 释放lock

写满

1. 当前goroutine(G1)会调用gopark函数,将当前协程置为waiting状态;
2. 将M和G1绑定关系断开;
3. scheduler会调度另外一个就绪态的goroutine与M建立绑定关系,然后M 会运行另外一个G。

从写满channel读取恢复

  • G2调用 t:=<-ch 获取一个元素;
  • 从channel的buffer里面取出一个元素task1;
  • 从sender等待队列里面pop一个sudog;
  • 将sender队列里的数据复制buffer中task1的位置,然后更新buffer的sendx和recvx索引值;
  • 这时候需要将G1置为Runable状态,表示G1可以恢复运行;

这个时候将G1恢复到可运行状态需要scheduler的参与。G2会调用goready(G1)来唤醒G1

读空channel

  • 将recvq中的task存入buffer;
  • goready(G2) 唤醒G2;

你可能感兴趣的:(语言开发,golang)