一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第20天,点击查看活动详情。
goroutine之间通信
在使用多个协程的时候,有些情况需要协程与协程之间进行通信。例如,一个协程需要使用另一个协程的数据。这就需要goroutine之间进行通信。常用的方式有两种,一种是借用全局变量,另一种是使用channel。
全局变量
这种方式是最简单的方式,通过共享全局变量实现goroutine之间的通信。但是这种方式传递的数据只能一写多读,并且需要使用读写锁或互斥锁对全局变量进行加锁。
var stop bool
func main() {
go f()
time.Sleep(time.Second)
stop = false
time.Sleep(time.Second)
fmt.Println("main done")
}
func f() {
for stop {
fmt.Println("still run")
time.Sleep(time.Second)
}
}
Channel
声明方式:
ch:=make(chan string)
这里面chan是一个关键字,表示是channel类型,string表示channel里的数据是string类型
channel有两种使用:
- 接收:获取chan中的值,操作符为<-chan
- 发送:向chan发送值,把值放在chan中,操作符为chan<-
func main() {
ch := make(chan string)
go func() {
fmt.Println("hello")
ch <- "goroutine"
}()
fmt.Println("main goroutine")
v := <-ch
fmt.Println("chan", v)
}
这里面程序完整执行了,因为make创建的chan中没有值,但是mian goroutine 又需要从chan中获取值,获取不到就会一直等待,等到另一个gorountine向chan中发送值。
无缓冲channel
在上面make出的channel是无缓冲channel,即容量为0,不能存储任何数据,只起到传输数据的作用,也可以称作同步channel。
有缓冲channel
有缓冲channel类似于一个可以阻塞的队列,先进先出,声明方式如下:
cacheCh := make(chan int, 5)
通过第二个参数可以指定容器的大小。
特点:
- 内部有一个缓冲队列
- 发送操作向队尾插入元素,如果队列已满,则阻塞等待,直到另一个goroutine执行接收操作释放队列空间。
- 接收操作从队列的头部获取元素并将它从队列里删除。如果队列为空,则阻塞等待,知道另一个goroutine执行发送操作插入新的元素。
cacheCh := make(chan int, 5)
cacheCh <- 2
cacheCh <- 3
fmt.Println("容量:", cap(cacheCh), "元素个数:", len(cacheCh))
close(cacheCh)
通过close来关闭通道。关闭的通道不可以发送数据(造成异常),但是可以接收数据。
单向channel
限制一个channel只可以接收但不能发送或者限制一个通道只可以发送不可以接收。
onlySend :=make(chan<-int)
onlyReceive := make(<-chan int)