前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Go语言学习(十一)| 通道

Go语言学习(十一)| 通道

作者头像
Mervyn
发布2020-07-21 14:55:06
2640
发布2020-07-21 14:55:06
举报

通道类型的值本身就是并发安全的,这也是 Go 语言自带的、唯一一个可以满足并发安全性的类型。

通道声明及初始化

通道相当于一个先进先出的队列。在声明一个通道类型变量的时候,我们首先要确定该通道类型的元素类型,决定了我们可以通过这个通道传递什么样的数据。

声明并初始化一个通道时需要使用内建函数 makemake 第二个参数可选,用于表示通道的容量。

代码语言:javascript
复制
ch := make(chan int, 2)

当容量为0时,该通道为 非缓冲通道 。大于0时为 缓冲通道

元素值的发送和接受都需要使用操作符 <- (也叫接送操作符)。用法如下:

  • ch <- 将元素发送至通道 ch
  • <- ch 也被叫做接收表达式,用于表达从该通道接收一个元素值。
代码语言:javascript
复制
package main

import "fmt"

func main() {
ch := make(chan int, 3)
ch <- 4//像通道ch 发送一个元素值2
ch <- 3
ch <- 1

elem := <- ch//从通道中接收一个元素值
fmt.Println(elem)//4
fmt.Println(len(ch))//2
}

引发阻塞的操作

缓冲通道

如果通道已满,那么所有的发送到该通道的操作都会被阻塞,直到通道中有元素被接收走。通道会优先通知最早因此等待的那个操作所在的goroutine,再次进行发送。

如果通道已空,那么对它的所有接收操作都会被阻塞,直到通道中有新元素出现。道会优先通知最早因此等待的那个操作所在的goroutine,再次进行接收。

非缓冲通道

无论是发送还是接收,一开始执行就会被阻塞,直到出现配对的操作也开始,才会继续传递。由此可见,非缓冲通道是采用同步的方式进行传递。

值为nil的通道

对值为nil的通道进行发送和接收都会永久阻塞状态。它们所属的goroutine中的代码都不再会执行。

由于通道类型是引用类型,所以它的零值就是 nil 。所以只声明了该类型的变量但是没有用make 初始化时,它的值就会是 nil

引发panic的操作

下边罗列一下会引发 panic 的情况:

  • 通道关闭后,继续向它进行发送操作
  • 试图关闭一个已经关闭的通道

千万不要让接收方关闭通道,应该让发送方做这件事

单向通道

只能发不能收或者只能收不能发的通道就是单向通道。例:

代码语言:javascript
复制
ch := make(chan<- int, 1)

上述通道表示只能发不能收。简称为 发送通道

通道常用操作

代码语言:javascript
复制
package main

import "fmt"

func main() {
ch := getChan()
for elem := range ch {
fmt.Println(elem)
}
}

func getChan()  <-chan int {
ch := make(chan int, 5)
for i := 0; i < 5; i++ {
ch <- i
}

close(ch)
return ch
}

下边例子为 select 与通道联合使用

代码语言:javascript
复制
package main

import (
"fmt"
"math/rand"
)

func main() {
channels := [3]chan int {
make(chan int, 1),
make(chan int, 1),
make(chan int, 1),
}

randInt := rand.Intn(3)
fmt.Println("value:", randInt)
channels[randInt] <- randInt

select {
case elem := <-channels[0]:
fmt.Println("first channel, value:", elem)
case elem := <-channels[1]:
fmt.Println("second channel, value:", elem)
case elem := <-channels[2]:
fmt.Println("third channel, value:", elem)
default:
fmt.Println("error")

}
}

select 有默认分支时不会被阻塞。

代码语言:javascript
复制
package main

import (
"fmt"
"time"
)

func main() {
ch := make(chan int, 1)
time.AfterFunc(time.Second, func() {
close(ch)
})

select {
case _, ok := <-ch://判断通道是否已关闭
if !ok {
fmt.Println("ch is closed")
break
}
}
}
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-03-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 开发技术那些事 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 通道声明及初始化
  • 引发阻塞的操作
    • 缓冲通道
      • 非缓冲通道
        • 值为nil的通道
        • 引发panic的操作
        • 单向通道
        • 通道常用操作
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档