channel作为Go语言最有特色的数据类型,和goroutine并驾齐驱,共同代表了Go语言独有的并发编程模式和编程哲学:

channel是后半句话的完美实现。我们可以利用通道在多个goroutine之间传递数据。

创建channel

c := make(chan int)

fmt.Println(len(c))  //0
fmt.Println(cap(c))  //0

make(chan int):chan是Go的关键字,表示通道,int说明了该通道的元素类型。

make(chan int , 2)
fmt.Println(len(c))  //0
fmt.Println(cap(c))  //2

这行代码表示创建一个容量为2的元素类型为int的通道。

通道的分类

当容量为0时,叫做非缓冲通道。当容量大于0时,叫做缓冲通道。

元素值的发送和接收都需要用到接送操作符<-

//发送表达式

c := make(chan int ,1)
c <- 1

//接收表达式

value,ok := <- c

对通道的发送和接收操作都有哪些基本的特性?

回答:

  1. 对于同一个通道,发送操作之间是互斥的,接收操作之间也是互斥的
  2. 发送操作和接收操作过程中对元素值的处理都是不可分割的
  3. 发送操作在完全完成之前会被阻塞。接收操作也是如此。

对于同一个通道,发送操作之间是互斥的,接收操作之间也是互斥的

同一时刻,有多个对同一个通道的发送操作,Go语言的运行时系统只会执行其中一个。知道这个元素值被复制进该通道之后,其他的针对该通道的发送操作才可能被执行。

类似的,在同一个时刻,运行时系统也是会执行对同一个通道的人一个接收操作中的某一个。直到这个元素值被完全移出该通道之后,其他的对该通道的接收操作才可能被执行。即使这些操作是并发执行的也是如此。

另外,对于通道中的同一个元素值来说,发送操作和接收操作之间也是互斥的。正在被复制但是还没有完全复制进通道的元素值,是不可能被想接收它的一方看到和取走。

元素值从通道进入外界时,这个操作包含了两步:

  • 生成通道中这个元素值的副本,并准备给到接收方
  • 删除通道中的这个元素值。

发送操作和接收操作过程中对元素值的处理都是不可分割的

发送操作要么还没有复制元素值,要么已经复制完毕,绝对不会出现只复制了一部分的情况。

接收操作的复制通道中的副本,将副本发送给接收方,删除通道中的元素值这一系列动作是一气呵成的,绝不会被打断。

发送操作在完全完成之前会被阻塞。接收操作也是如此。

发送操作

一般情况下,发送操作包括了“复制元素副本”和“放置副本到通道内部”这两个步骤,这两个步骤完全完成之前,发起这个发送操作的那句代码会一直阻塞在那里。这句代码之后的代码不会有执行的机会,直到这句代码的阻塞解除。

更标准的说法是:在通道完成发送操作之后,运行时系统会通知这句代码所在的goroutine,使这个goroutine去争取继续运行代码的机会。

接收操作

接收操作通常包含了“复制通道内的元素值”“放置副本到接收方”“删掉原值”三个步骤。

在所有这些步骤完全完成之前,发起该操作的代码也会一直阻塞,直到该代码所在的 goroutine收到了运行时系统的通知并重新获得运行机会为止。

发送操作和接收操作在什么时候可能被长时间的阻塞?

缓冲通道

通道已满:

对它的所有发送操作都会被阻塞,发送操作所在的goroutine会顺序地进入通道内部的“发送等待队列”,直到通道中有元素被接收。这时,通道会优先通知最早等待发送的goroutine,这个goroutine会再次执行发送操作。

通道已空

对它的所有接收操作都会被阻塞,接收操作所在的goroutine会按照先后顺序被放入通道内部的“接收等待队列”,直到通道中有新的元素值出现。通道就会通知最早等待的那个接收操作所在的goroutine,并使它再次执行接收操作。

非缓冲通道

无论是发送操作还是接收操作,一开始执行就会被阻塞,直到配对的操作也开始执行,才会继续传递。并且,数据是直接从发送发复制到接收方的,中间不会用非缓冲通道做中转。

值为nil的通道

不论它的具体类型是什么,对它的发送操作和接收操作都会永久地处于阻塞状态,它们所属的goroutine中的任何代码,都不再会别执行。

发送操作和接收操作在什么时候会引发panic?

  1. 通道一旦关闭,再对它进行发送操作,就会引发panic。
  2. 如果试图关闭一个已经关闭了的通道,也会引发panic。
value, ok := <- c  //接收表达式

如果ok是false,说明通道已经关闭,并且没有元素可取了。

有一种情况:通道关闭时,里面还有元素未被取出,那么接收表达式的第一个结果,仍会是通道中的某一个元素,第二个结果一定是true。

考虑到通道的收发操作有如上的特性,所以除非有特殊的保障措施,否则,我们千万不要让接收方关闭通道,而应该让发送发关闭通道。

12-06 01:00