go-channel
Golang中channel总结
golang里面倡导的是“通过通信来共享内存”,简单来说channel是groutine之间通信的桥梁。
1. 基本操作
- 声明
1 | // 无缓冲channel |
- 发送与接收
1 | ch := make(chan int) |
注意:channel是goroutine之间通信的机制
1
2
3
4
5
6
7
8
9
10
11package main
import "fmt"
func main() {
ch := make(chan int)
ch <- 1
fmt.Println(<-ch)
}报错:
fatal error: all goroutines are asleep - deadlock!
channel是不同goroutine之间通信的机制,同一个goroutine里面发送和接收是会造成阻塞的。修改如下就没问题了:
1
2
3
4
5
6
7
8
9
10
11
12
13package main
import "fmt"
func main() {
ch := make(chan int)
go func() {
ch <- 1
}()
fmt.Println(<-ch)
}
2. 无缓冲channel与有缓冲channel
2.1 关于阻塞
- 无缓冲channel的发送和接收是同步的
- 无缓冲channel的接收在发送之前阻塞
- 在已经包含数据的无缓冲channel的发送在接收之前阻塞
- 有缓冲channel的发送和接收可以是异步的
- 当有缓冲channel队列满了时,发送阻塞
- 当有缓冲channel为空时,接收阻塞
2.2 关闭之后操作
重复关闭 channel 会导致 panic。
1
panic: close of closed channel
向关闭的 channel 发送数据会 panic。
1
panic: send on closed channel
从关闭的 channel 读数据不会 panic,读出 channel 中已有的数据之后再读就是 channel 类似的默认值,比如 chan int 类型的 channel 关闭之后读取到的值为 0。
3. channel死锁的几种常见情况
同一个goroutine中对同一个channel进行读写操作(上面已经提到)。
1
2
3
4
5
6
7
8
9
10
11package main
import "fmt"
func main() {
ch := make(chan int)
ch <- 1
fmt.Println(<-ch)
}在两个以上的goroutine中,channel的读写早于goroutine的创建。
1
2
3
4
5
6
7
8
9
10
11
12
13
14package main
import (
"fmt"
)
func main() {
ch := make(chan int)
ch <- 12
go func() {
fmt.Println(<-ch)
}()
}多个goroutine使用多个channel进行通信,channel之前互相等待对方状态造成死锁。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22package main
func main() {
ch1 := make(chan int)
ch2 := make(chan int)
go func() {
for {
select {
case <-ch1:
ch2 <- 2
}
}
}()
for {
select {
case <-ch2:
ch1 <- 1
}
}
}
3. channel常见用法
3.1 通信
1 | package main |
3.2 range遍历
1 | package main |
range c
产生的迭代值为Channel中发送的值,它会一直迭代直到channel被关闭。上面的例子中如果把close(c)
注释掉,程序会一直阻塞在for …… range
那一行。
3.3 超时处理
1 | package main |
使用time.After()返回一个类型为<-chan Time
的单向的channel。