在Golang中goroutine之间通过通道机制传递值来控制并发并实现同步通信。可以使用内置函数make创建一个新的通道对象。

//不带缓冲的int类型的通道
ic := make(chan int)
 
//可以容纳10个字符串的带缓冲的通道

sc := make(chan string, 10)

 

要在通道上发送数据,可以使用 <- 符号作为二进制操作符。若要在通道上接收数据,可以将这个符号用作一元运算符。示例如下:

ic <- 3   //向通道中发送数值3

n := <-sc //从通道中接收一个字符串并赋值给n变量

 

操作符 <- 用于指定通道的方向,发送或接收。如没有给出方向,则表示通道是双向的。

chan int    //可以用来发送和接收类型为int的值
chan<- string //只能用来发送字符串类型的数据

<-chan int    //只能用于接收整型

 

缓冲和非缓冲通道

  • 如果通道的容量为零,则表示通道没有设置缓冲区,当发送方发送数据后将阻塞,直到接收方接收到该数据。

  • 如果通道有缓冲区,发送方仅在将值复制到缓冲区之前阻塞,复制完毕即返回;如果缓冲区已满,则意味着要等到某个接收器接收一个值,才会解除阻塞状态。

  • 接收方直到有收到数据前,总是处于阻塞状态。

  • 向空通道(Nil Channel)发送或接收数据将导致永远处于阻塞状态。

 

关闭通道

调用close函数意味着不再在通道上发送任何值。需要注意的是,只有在接收方确定要关闭通道时才应当关闭通道。

  • 在调用close之后,以及在接收到任何之前发送的值后,receive操作将返回一个零值,而不会阻塞。

  • 多值接收操作会返回通道是否关闭的标识。

  • 发送或关闭一个已经被关闭的通道将会导致运行时异常(runtime panic)。关闭空通道也会导致运行时异常。

ch := make(chan string)
go func() {
    ch <- "Hello!"
    close(ch)
}()
 
fmt.Println(<-ch) // 输出 "Hello!"
fmt.Println(<-ch) // 输出空值 "" 并且不会阻塞
fmt.Println(<-ch) // 再次输出空值 ""
v, ok := <-ch     // v变量值为"", ok变量值为false
 
// 在ch通道关闭前始终从通道中接收数据
for v := range ch {
    fmt.Println(v) // 这行代码不会被执行

}

 

示例

在下面的示例中,Publish函数将返回一个通道,该通道用于在文本发布后广播消息。

//Publish在给定的时间过期后将文本打印到stdout。
// 它将在text发布后关闭等待通道。
func Publish(text string, delay time.Duration) (wait <-chan struct{}) {
    ch := make(chan struct{})
    go func() {
        time.Sleep(delay)
        fmt.Println(text)
        close(ch)
    }()
    return ch
}

注意,这里使用了一个由空结构组成的通道来表示该通道将仅用于发出信号,而不是传递数据。可以这样使用这个函数。

wait := Publish("important news", 2 * time.Minute)
// 执行其他业务逻辑
<-wait // 这行代码将保持阻塞状态,直到读取到数据为止