1. Channel是什么,怎样使用它?
Channel类似Unix管道,多个goroutine利用channel通信来传递数据,channel和goroutine一同构成了Go语言特有的CSP(communicating sequential processes,通信顺序进程)并发模型,goroutine是CSP模型中的实体,channel是通道。
makenilmake
chan T // 可以对通道发送和接收类型T的值
chan<- float64 // 可以对通道发送类型float64的值
<-chan int // 可以对通道接收类型int的值
int
c1 := make(chan int) // int类型数据的非缓冲通道
c2 := make(chan int, 0) // int类型数据的非缓冲通道
c3 := make(chan *os.File, 10) // 文件指针类型的缓冲通道
<-
ch := make(chan int, 3)
ch <- 1 // 向通道发送元素值
elem := <-ch // 从通道接收元素值,并赋值给变量
2. Channel的接收和发送有哪些基本特性?
对于同一个channel,接收操作之间是互斥的,发送操作之间也是互斥的
- 对于通道中的同一个值,接收操作和发送操作是互斥的
- 在同一时刻,runtime只会执行对同一个通道的任意接收操作中的某一个,发送操作同理
- 发送元素值的时候,外界元素会被复制,进入通道的是元素的副本,直到元素值被完全复制进入通道,其他对通道的发送操作才能执行
- 接收元素值的时候,通道中的元素会被复制,生成副本,再准备给接收放,然后删除通道中的元素值,直到元素值被完全移除掉,其他对通道的接收操作才能执行
接收和发送操作对元素值的操作都是原子性的,不可分割:
- 处理元素值的过程是不可打断的。发送操作要么没有复制数据,要么复制数据完毕,只有这两种状态,不会出现只复制一部分的中间状态;接收操作也是,绝不会出现元素值残留在通道的情况
- 这个特性保证了元素值的完整性,确保了操作的唯一性,对于一个元素值来说,它只能被某一个发送操作放入通道中,也只能被某一个接收操作从通道中取出
发送操作和接收操作在完成之前会被阻塞:
- 发送操作有两个步骤:「复制元素值」、「将副本放入通道」,在这两个步骤完成前,发送操作所在的goroutine会被阻塞
- 接收操作有三个步骤:「复制通道中的元素」、「移动副本到接收方」、「删除原值」,接收操作所在的goroutine会被阻塞
- 直到所有操作完成,操作所在goroutine会接收到runtime的通知,并获得运行机会
- 阻塞就是为了实现操作的互斥性和保证元素完整性。
3. 缓冲通道和非缓冲通道的区别?
缓冲通道:
- 当通道已满,所有对它的发送操作都会被阻塞,直到通道中有元素被取走
- 由于通道内有发送等待队列,被阻塞发送操作所在的goroutine会按顺序进入等待队列,所以最早等待的goroutine会被优先通知
- 当通道已空,所有对它的接收操作都会被阻塞,直到通道中有新元素放入
- 由于通道内有接收等待队列,因此而等待的goroutine都会按顺序进入等待队列,最早等待的goroutine会被最先通知
- 大多数情况下,通道作为收发双方的中间件,元素值会先从发送方复制到缓冲通道,再从缓冲通道复制接收方。到但是当发送操作执行时,发现空的通道中刚好有等待的接收操作,会直接把元素值复制给接收方
- 缓冲通道使用异步方式传递数据
非缓冲通道:
- 无论是接收操作还是发送操作,一开始执行就会被阻塞,直到另一方配对操作开始执行,才会传递数据,也就是通道两侧接收方和发送方同时存在并进行操作,才会进行数据传输
- 数据是直接从发送方复制到接收方的,没有使用通道做中转
- 非缓冲通道使用异步的方式传递数据
ch1 := make(chan int, 1)
ch1 <- 1
ch1 <- 2 // 通道已满,操作阻塞
ch2 := make(chan int, 1)
elem, ok := <-ch2 //通道为空,操作阻塞
边界性:
通道有边界性,缓冲通道和非缓冲通道可以通过边界性理解为一种形式。通道有两个边界,一个是通道已满,阻塞发送操作;一个是通道已空,阻塞接收操作。非缓冲通道可以理解成是一个一直处于通道已满状态的缓冲通道。
4. 由于错误使用通道导致阻塞的情况?
对值为nil的通道进行发送操作或接收操作多会被阻塞,操作所属的goroutine都不会继续执行。
var ch chan int
ch <- 1 // 阻塞
<-ch // 阻塞
5. 在什么情况下,发送操作和接收操作会发生panic?
- 通道关闭,对其进行发送操作会引发panic
- 关闭已经关闭的通道会引发panic
- 接收操作可以感知到通道关闭,能够安全退出
elem, ok := <-ch // 第二个变量ok为bool类型,如果ok为true,表示通道已关闭,并且再没有元素可取
- 用第二个变量来判断通道是否关闭会有延迟,因为当通道已经关闭,但是通道里还有元素未取出时,第二个变量还会为true,并且元素可以取出
6. 通道的长度代表什么?它在什么时候与通道容量相同?
通道的长度代表通道当前含有的元素个数,在通道已满的状态下,会与容量相同
7. 元素在通道中传递会进行复制,该复制是深度拷贝吗?
不是深度拷贝,在Go中没有深度拷贝
8. 通道分为双向通道和单向通道,单向通道有什么作用?
双向通道既能发又能收