您的位置 首页 golang

golang channel使用总结

channel可以理解成一个管道,通过它可以发送或者接收数据进行通信

1、通过for range读取channel

当需要持续从channel读取数据时,可以通过for-range读取,当channel关闭时,for循环会自动退出,不需要主动监测channel是否关闭

 // 示例
func main() {
c := make(chan int, 10)
go func() {
for i := 0; i < 10; i++ {
c <- i
}
close(c)
}()
for val := range c {
fmt.Println(val)
}
fmt.Println("end")
}  

2、通过_,ok判断channel是否关闭

当读取的channel不确定是否关闭时,可以使用_,ok进行监测。如果ok=true表示通道没有关闭,反之通道关闭,从已关闭的channel中读取数据会得到零值

 // 示例
func main() {
c := make(chan int)
go func() {
for i := 0; i < 5; i++ {
c <- i
}
close(c)
}()
for  {
if val, ok := <-c; ok {
fmt.Println(val)
} else {
// 通道关闭,此时读取的是零值
fmt.Println(<-c)
break
}
}
fmt.Println("end")
}  

3、基于select的多路复用

select可以同时监控多个通道。当通道为nil时,对应的case永远阻塞

select语句不是循环,它只会选择一个case来处理

如果没有case需要处理,select语句就会一直阻塞

 // 示例
func main() {
c := make(chan string)
go func() {
c <- "send1"
// 5s后再向channel中发送一条数据
time.Sleep(time.Second * 5)
c <- "send2"
}()
Loop:
for {
select {
case v := <-c:
fmt.Println(v)
case <-time.After(time.Second * 3):
// 3s后超时,跳出for循环
fmt.Println("timeout")
break Loop
}
}
fmt.Println("end")
}  

4、channel读写权限控制

可以对指定的channel设置为只读或只写权限

chan T // 可以接收和发送类型为 T 的数据

chan<- float64 // 只可以发送 float64 类型的数据

<-chan int // 只可以接收 int 类型的数据

 // 示例
func onlyWrite(num int) <-chan int {
oc := make(chan int)
go func() {
for i := 0; i < num; i++ {
oc <- i
}
close(oc)
}()
return oc
}
func onlyRead(ic <-chan int) {
for x := range ic {
fmt.Println(x)
}
}
func main() {
onlyRead(onlyWrite(10))
}  

5、有缓冲和无缓冲的channel

无缓冲channel,channel发送和接收动作是同时发生, 如果没有goroutine读取,那么发送者就会一直阻塞

有缓冲channel类似一个队列,只有队列满了才会发送阻塞

 // 无缓冲(第二个参数为0,或者不写第二个参数)
c2 := make(chan int, 0)
// 有缓冲
c3 := make(chan int, 1)

// 示例(无缓冲)
func main() {
ch := make(chan int)
// 如果将发送数据放在这里,由于同时没有接收者,发生了阻塞
  // 报错:fatal error: all goroutines are asleep - deadlock!
// ch <- 1

go func() {
for {
select {
case i := <-ch:
fmt.Println(i)
}
}
}()
ch <- 1
}
// 示例(有缓冲)
func main() {
ch := make(chan int, 2)
ch <- 1
ch <- 2
// 由于设置了channel的缓冲大小为2,往ch里发送超过2个数据时会阻塞
// 报错:fatal error: all goroutines are asleep - deadlock!
//ch <- 3
go func() {
for {
select {
case i := <-ch:
fmt.Println(i)
}
}
}()
time.Sleep(time.Second * 5)
}  

6、WaitGroup + channel

可以使用WaitGroup进行多个channel的同步,WaitGroup可以保证在并发环境中完成指定数量的任务,sync.WaitGroup是并发安全的

 # 示例
var wg sync.WaitGroup
func main() {
var num = 3
tc := make(chan string, num)
wg.Add(num)
for i := 0; i < num; i++ {
go start(tc, i)
}
for i := 0; i < num; i++ {
tc <- strconv.Itoa(i)
}
// 关闭通道
close(tc)
  
// 等待完成
wg.Wait()
}
func start(tc chan string, worker int) {
defer wg.Done()
for {
// 等待
v, ok := <-tc
if !ok {
// 通道已关闭,退出
fmt.Println("closed", v, worker)
return
}
    
// 开始执行
fmt.Println("started", v, worker)
    
// 具体执行逻辑
// todo
    
// 完成
fmt.Println("finished", v, worker)
}
}  

文章来源:智云一二三科技

文章标题:golang channel使用总结

文章地址:https://www.zhihuclub.com/97072.shtml

关于作者: 智云科技

热门文章

网站地图