Channel是Go语言在语言级别提供的goroutine间的通信方式。我们可以用channel在两个或多个goroutine之间传递消息。channel是进程内的通信方式,因此通过channel传递对象的过程和调用函数是的参数传递行为比较一致,比如也可以传递指针等。如果需要跨进程通信,建议用分布式系统的方法来解决,比如用socket或HTTP等通信协议。
channel是类型相关的,也就是说,一个channel只能传递一种类型的值。这个类型需要在声明channel时指定。
一般channel的声明形式为:
var chanName chan ElementType
举个栗子,我们声明一个传递类型为int的channel:
var ch chan int
或者声明一个map,元素是bool的channel:
var m map[string] chan bool
定义一个channel,直接使用make()函数即可:
ch := make(chan int)
在channel的用法中,最常见的包括写入和读出,将一个数据写入channel的语法很直观:
ch <- value
向channel写入数据通常会导致程序阻塞,直到有其他goroutine从这个channel中读取数据,从channel中读取数据的语法是:
value := <- ch
示例:
在这个例子中,我们定义了一个10个channel的数组chs,并把数组中的每个channel分配给10个不同的goroutine,在每个goroutine的print完成后,我们通过ch <- 1向channel中写入一个数据,在这个channel被读取前,这个操作是阻塞的。在所有goroutine启动完成后,我们通过<-ch从10个channel中依次读取数据,在数据写入channel前,这个操作也是阻塞的。这样就实现了锁的功能。
package main import (
"fmt"
"time"
) func Count(ch chan int) {
fmt.Println("Counting")
time.Sleep(time.Second)
ch <- 1
} func main(){
chs := make([]chan int,10)
for i := 0;i<10;i++ {
chs[i] = make(chan int)
go Count(chs[i])
} for _,ch := range(chs){
<-ch
}
}
坑:
channel默认上是阻塞的,也就是说,如果Channel满了,就阻塞写,如果Channel空了,就阻塞读。于是,我们就可以使用这种特性来同步我们的发送和接收端。
channel <-,发送一个新的值到通道中 <-channel,从通道中接收一个值,这个更像有两层含义,一个是会返回一个结果,当做赋值来用:msg := <-channel;另外一个含义是等待这个channel发送消息,所以还有一个等的含义在.所以如果你直接写fmt.Print(<-channel)本意只是想输出下这个chan传来的值,但是其实他还会阻塞住等着channel来发.
默认发送和接收操作是阻塞的,直到发送方和接收方都准备完毕。
package main import "fmt" func main() {
messages := make(chan string)
go func() {
messages <- "ping"
}()
msg := <-messages
fmt.Println(msg)
} //output:ping
如果是下面这样写,就会报错,因为msg还在等待message来传送值
func main() {
messages := make(chan string) msg := <-messages messages <- "ping"
fmt.Println(msg)
} //fatal error: all goroutines are asleep - deadlock! //goroutine 1 [chan receive]: