channel基本用法
# 使用通道
ch <- x // 发送语句
x := <-ch // 赋值语句
<-ch // 接收语句,丢弃结果
# 创建通道
ch := make(chan int) // 无缓冲通道
ch := make(chan int, 0) // 无缓冲通道
ch := make(chat int, 3) // 容量为3的缓冲通道
应用场景:
- 无缓冲通道:用于发送方协程和接收方协程
同步化
- 有缓冲通道:提高性能
单向通道类型
Go的类型系统提供单项通道类型,仅发送或仅接收。
需要注意的是双通道类型(即chan int这种)可以转为发送通道类型(chat<- int)、接收通道类型(int <- chan int),但单向通道类型不能转双通道
chat<- int // 只发送通道
<-chat int // 只接收通道,不能使用close将其关闭,因为close只能在发送方调用
例子:
func main() {
naturals := make(chan int)
squares := make(chan int)
go counter(naturals)
go squarer(naturals, squares)
printer(squares)
}
func counter(out chan<-int) {
for x := 0; x < 100; x++ {
out <- x
}
close(out)
}
func squarer(in<-chan int, out chan<- int) {
for v := range in {
out <- v * v
}
close(out)
}
func printer(in <-chan int) {
for v := range in {
fmt.Println(v)
}
}
阻塞场景
无缓冲通道
特点,发送的数据被接收了,才算完成。
阻塞场景:
- 通道中无数据,向通道写数据,但
没有其它携程读取
- 通道中无数据,向通道读数据
例子:
// 情形一
func main() {
c := make(chan string)
c <- "hello world" // 执行到该步会一直阻塞,因为接收方和发送发是同一个携程,始终没有接收方接收,进而导致报错
msg := <- c
}
// 情形二
func main() {
c := make(chan string)
fmt.Println(<-c)
}
// output
fatal error: all goroutines are asleep - deadlock!
有缓冲通道
特点:有缓存空间时,发送方会向通道发送内容然后直接返回(即非阻塞)
,缓存中有数据时从通道中读取数据直接返回
。
阻塞场景:
- 通道
已满
,执行写通道,但无携程读。 - 通道缓存无数据,但执行读通道。
// 情形一
func main() {
c := make(chan string, 2)
c <- "hello"
c <- "world"
c <- "golang"
fmt.Println(<-c)
}
// 情形二
func main() {
c := make(chan string, 2)
fmt.Println(<-c)
}
# output
fatal error: all goroutines are asleep - deadlock!
非阻塞写法
无缓冲通道
方式1:先写接受者
func main() {
c := make(chan string)
// 接收者
go func() {
v := <-c
fmt.Println(v)
}()
// 发送者
c <- "pig"
}
方式2:先写发送者
func main() {
c := make(chan string)
// 发送者
go func() {
c <- "pig"
}()
// 接收者
fmt.Println(<-c)
}
有缓冲通道
使用select解决多个协程读通道阻塞问题
func main() {
c1 := make(chan string)
c2 := make(chan string)
go func() { // 协程1
for {
c1 <- "Event 1s"
time.Sleep(1 * time.Second)
}
}()
go func() { // 协程2
for {
c2 <- "Event 2s"
time.Sleep(2 * time.Second)
}
}()
for {
// fmt.Println(<-c1) // 如果c1读通道被阻塞,则导致c2执行不了读通道
// fmt.Println(<-c2) // 同理如上
select {
case msg1 := <-c1:
fmt.Println(msg1) // case默认break
case msg2 := <-c2:
fmt.Println(msg2)
}
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。