用途
不要通过共享内存来通信,要通过通信来共享内存。后通道就是后半句的完美实现。
特点
go自带的唯一一个支持并发安全的数据类型。
一个通道相当于一个先进先出(FIFO)的队列。也就是说,通道中的各个元素值都是严格地按照发送的顺序排列的,先被发送通道的元素值一定会先被接收。
类型
缓冲通道
非缓冲通道
todo:底层实现?
通道的发送和接收特点
对于同一个通道,发送操作之间是互斥的,接收操作之间也是互斥的(并发安全)。
发送操作和接收操作中对元素值的处理都是不可分割的(支持原子性)。
发送操作在完全完成之前会被阻塞。接收操作也是如此。
发送过程
发送操作包括了“复制元素值”和“放置副本到通道内部”这两个步骤。
接收过程
接收操作通常包含了“复制通道内的元素值”“放置副本到接收方”“删掉原值”三个步骤。
todo:底层实现?
发送操作和接收操作在什么时候可能被长时间的阻塞?
先说针对缓冲通道的情况。如果通道已满,那么对它的所有发送操作都会被阻塞,直到通道中有元素值被接收走。这时,通道会优先通知最早因此而等待的、那个发送操作所在的 goroutine,后者会再次执行发送操作。
由于发送操作在这种情况下被阻塞后,它们所在的 goroutine 会顺序地进入通道内部的发送等待队列,所以通知的顺序总是公平的。
相对的,如果通道已空,那么对它的所有接收操作都会被阻塞,直到通道中有新的元素值出现。这时,通道会通知最早等待的那个接收操作所在的 goroutine,并使它再次执行接收操作。因此而等待的、所有接收操作所在的 goroutine,都会按照先后顺序被放入通道内部的接收等待队列。
对于非缓冲通道,情况要简单一些。无论是发送操作还是接收操作,一开始执行就会被阻塞,直到配对的操作也开始执行,才会继续传递。
由此可见,非缓冲通道是在用同步的方式传递数据。也就是说,只有收发双方对接上了,数据才会被传递。并且,数据是直接从发送方复制到接收方的,中间并不会用非缓冲通道做中转。相比之下,缓冲通道则在用异步的方式传递数据。在大多数情况下,缓冲通道会作为收发双方的中间件。正如前文所述,元素值会先从发送方复制到缓冲通道,之后再由缓冲通道复制给接收方。
但是,当发送操作在执行的时候发现空的通道中,正好有等待的接收操作,那么它会直接把元素值复制给接收方。
通道的底层实现
todo:环形链表
单向通道
单向发通道 make(chan<- int)
单向收通道 make(<-chan int)
单向通道作用
约束(入参和出参)
获取元素
for
select