从零学习go语言(八)

101 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 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有两种使用:

  1. 接收:获取chan中的值,操作符为<-chan
  2. 发送:向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)

通过第二个参数可以指定容器的大小。

特点:

  1. 内部有一个缓冲队列
  2. 发送操作向队尾插入元素,如果队列已满,则阻塞等待,直到另一个goroutine执行接收操作释放队列空间。
  3. 接收操作从队列的头部获取元素并将它从队列里删除。如果队列为空,则阻塞等待,知道另一个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)