channel有点类似于管道,它在goroutine同步与通信中,有着起承转合的作用,同时也是Golang实现CSP模型的关键
package main
func main() {
senderOnly := make(chan<- int) // 只能用来发送(管道的入口,只进不出)
receiverOnly := make(<-chan int) // 只能用来接收(管道的出口,只出不进)
unbuffer := make(chan int) // 无缓冲可收发
buffer := make(chan int, 2) // 有缓冲可收发(缓冲区长度为2)
println(senderOnly, receiverOnly, unbuffer, buffer)
}
以下是channel的一些使用场景:
等待goroutine完成
package main
func main() {
println("start main")
ch := make(chan bool)
go func() {
println("come into goroutine")
ch <- true
}()
println("do something else")
<-ch
close(ch)
println("end main")
}
输出结果
do something else
come into goroutine
end main
多个goroutine协同
三个功能不相关的goroutine最后结果要汇总累加到result上
package main
func main() {
println("start main")
ch := make(chan int)
var result int
go func() {
println("come into goroutine1")
var r int
for i := 1; i <= 10; i++ {
r += i
}
ch <- r
}()
go func() {
println("come into goroutine2")
var r int = 1
for i := 1; i <= 10; i++ {
r *= i
}
ch <- r
}()
go func() {
println("come into goroutine3")
ch <- 11
}()
for i := 0; i < 3; i++ {
result += <-ch
}
close(ch)
println("result is:", result)
println("end main")
}
其中一组打印结果:
start main
come into goroutine3
come into goroutine2
come into goroutine1
result is: 3628866
end main
Select
两个goroutine无直接关联,但其中一个先达到某一设定条件便退出或超时退出
package main
import "time"
func main() {
println("start main")
cond1 := make(chan int)
cond2 := make(chan uint64)
go func() {
for i := 0; ; i++ {
cond1 <- i
}
}()
go func() {
var i uint64
for ; ; i++ {
cond2 <- i
}
}()
endCond := false
for endCond != true {
select {
case a := <-cond1:
if a > 99 {
println("end with cond1")
endCond = true
}
case b := <-cond2:
if b == 100 {
println("end with cond2")
endCond = true
}
case <-time.After(time.Microsecond):
println("end with timeout")
endCond = true
}
}
println("end main")
}
其中打印结果有可能是:
start main
end with cond1
end main
也有可能是:
start main
end with timeout
end main
也可能是:
start main
end with cond2
end main
这说明循环100次大概需要1微秒的时间
channel与range
package main
import "fmt"
func main() {
println("start main")
ch := make(chan int, 4)
go func() {
for i := 0; i < 10; i++ {
ch <- i
}
// 如果不关闭channel,会引发panic
close(ch)
}()
for v := range ch {
fmt.Println(v)
}
println("end main")
}
打印结果为:
start main
0
1
2
3
4
5
6
7
8
9
end main
无缓冲channel
package main
func main() {
var ch = make(chan int)
ch <- 1
println(<-ch)
}
在playground中运行
打印结果为:
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.main()
/tmp/sandbox117018544/main.go:5 +0x60
死锁了,为什么会这样呢,因为ch是一个无缓冲的channel,在执行到ch <- 1就阻塞了当前goroutine(也就是main函数所在的goroutine),后面打印语句根本没机会执行
稍加修改即能正常运行
package main
func main() {
var ch = make(chan int)
go func() {
ch <- 1
println("sender")
}()
println(<-ch)
}
因为此时ch既有发送也有接收而且不在同一个goroutine里面,此时它们不会相互阻塞