Channel是在goroutine之间同步的方式
定义
ChannelType = ( "chan" | "chan" "<-" | "<-" "chan" ) ElementType .
它包括三种类型的定义。可选的<-代表channel的方向。如果没有指定方向,那么Channel就是双向的,既可以接收数据,也可以发送数据例子:
申明:ch chan int
创建对象:ch := make(chan int) 或者 ch := make(chan int, 100) // 100就是容量,即这个管道里可以存100个int
收发
SendStmt = Channel "<-" Expression .
Channel = Expression .例子:
c := make(chan int)
defer close(c)
发给channel:c <- 1+2
收消息:i := <-c
注意:
没有缓存的channel如果接收端没准备好发送程序不执行;如果有缓存且缓存满了也不执行;
如果channel被close了,发送数据的程序会出错(run-time panic)
如果channel被close了,接受数据的程序会直接返回,接收完已发送的数据后会返回元素类型的零值(zero value)。
比如:
x, ok := <-ch
x, ok = <-ch
第一次收到数据,第二次数据空了,如果ok是false,表明接收的x值是产生的零值,这个channel被关闭了或者空
阻塞
import "fmt"
func sum(s []int, c chan int) {
sum := 0
for _, v := range s {
sum += v
}
c <- sum // send sum to c
}
func main() {
s := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(s[:len(s)/2], c) go sum(s[len(s)/2:], c)
x, y := <-c, <-c // receive from c
fmt.Println(x, y, x+y)
}
迭代
range c产生的迭代值为Channel中发送的值,它会一直迭代直到channel被关闭。上面的例子中如果把close(c)注释掉,程序会一直阻塞在for …… range那一行。
func main() {
go func() { time.Sleep(1 * time.Hour) }()
c := make(chan int) go func() { for i := 0; i < 10; i = i + 1 { c <- i } close(c) }()
for i := range c { fmt.Println(i) }
fmt.Println("Finished")
}
SELECT
select语句选择一组可能的send操作和receive操作去处理。它类似switch,但是只是用来处理通讯(communication)操作。
case可以是send语句,也可以是receive语句,亦或者default。
receive语句可以将值赋值给一个或者两个变量。它必须是一个receive操作。
最多允许有一个default case,它可以放在case列表的任何位置,大部分会将它放在最后。import "fmt"
func fibonacci(c, quit chan int) {
x, y := 0, 1
for {
select {
case c <- x:
x, y = y, x+y
case <-quit:
fmt.Println("quit")
return
}
}
}
func main() {
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 10; i++ { fmt.Println(<-c) }
quit <- 0
}()
fibonacci(c, quit)
}
如果有同时多个case去处理,比如同时有多个channel可以接收数据,那么Go会伪随机的选择一个case处理(pseudo-random)。
如果没有case需要处理,则会选择default去处理,如果default case存在的情况下。如果没有default case,则select语句会阻塞,直到某个case需要处理。
需要注意的是,nil channel上的操作会一直被阻塞,如果没有default case,只有nil channel的select会一直被阻塞。select语句和switch语句一样,它不是循环,它只会选择一个case来处理,如果想一直处理channel,可以在外面加一个无限的for循环
超时
上面例子中如果没有消息,则一直会阻塞在select,所以需要加个timeout
go func() { time.Sleep(time.Second * 2) c <- "result 1" }()
关于时间的Channel
Timer:
timer1 := time.NewTimer(time.Second * 2)
<-timer1.C
fmt.Println("Timer 1 expired")
stop1 := timer1.Stop()
if stop1 { fmt.Println("Timer1 stop!") }Ticker:
ticker := time.NewTicker(time.Millisecond * 500)
go func() { for t := range ticker.C { fmt.Println("Tick at", t) }}()
stop1 := ticker1.Stop()
if stop1 { fmt.Println("Ticker1 stop!") }
CLOSE
close后:
如果继续往队列里发送数据,会导致:panic: send on closed channel
如果继续从队列里读数据,会可以不停读取,value, ok <- c,value会一直为0,ok会是false
网友评论