栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Go语言

Golang亿点小细节之close()

Go语言 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Golang亿点小细节之close()

@Golang亿点小细节之close()
你所忽略的,往往才是Bug的起源

close() 前置知识

1.不能去close()一个已经close()的channel

package main

func main(){
	ch := make(chan int)
	close(ch)
	close(ch)
	// output:
	// panic: close of closed channel
}

2.channel被close()后,不可以写入(注意会panic:send on closed channel),但可以读取。读取规则是如果有缓存值则读缓存值,没有缓存值则读零。注意:v, ok := <-ch中ok并不能去判断channel在何时关闭

package main

import "fmt"

func main() {
	ch := make(chan int,3)
	for i := 0; i < 3; i++ {
		ch <- i
	}
	close(ch)
	for i := 0; i < 5; i++ {
		v, ok := <-ch
		fmt.Println(v, ok)
	}
	fmt.Println("如果没有下一行就是写入失败被阻塞了啊")
	ch <- 1
	fmt.Println("我是下一行")
	
}

注意:v, ok := <-ch中ok并不一定能去判断channel在何时关闭

package main

import "fmt"

func main() {
	ch := make(chan int, 3)
	for i := 1; i < 4; i++ {
		ch <- i
	}
	close(ch)
	for i := 0; i < 4; i++ {
		v, ok := <-ch
		fmt.Println(v,ok)
	}
	
}

为什么说使用ok模式去判断channel是否关闭是不可靠的呢?
如上输出所示,在通道内存在缓存值时,即使channel被关闭了,ok的值仍然是true,要记得0和false才是一对哦!当channel被关闭且无缓存值时,ok才是false,同时使用channel时,最好在不需要使用的时候关闭,如果所有的goroutine都被阻塞了,会panic

上菜

下面的代码是脑爆Go语言群友昙花逐月提供,他提出问题,三个协程都有close(),为何没有输出
panic: close of closed channel,详情看注释哈

package main

import (
   "fmt"
)

func main() {
   a := 0
   fn := func() int {
      a++
      return a
   }
   ch := make(chan int, 1)
   chh := make(chan int, 3)
   for i := 0; i < 3; i++ {
      go func(j int) {
         for {
            ch <- 1
            n := fn()
            if n > 100 {
            	// 当第一个协程进入这段代码区域时,chh最终会被关闭
            	// 剩下的两个协程因为chh被关闭,在写入按理来说应该会报panic: send on closed channel
            	// 然而并未出现,原因在于其实协程在ch <- 1 的位置被阻塞了,为何?
            	// ch容量为1,在第一个协程退出的时候并为将ch中的值取出,导致被阻塞
               chh <- 1
               close(chh)
               return
            }
            fmt.Println("go", j, n)
            <-ch
         }
      }(i)
   }
   for i := 0; i < 3; i++ {
   	// 那这样是不是代表这chh中只有一个值,是的!但是为何主进程没阻塞呢?
   	// 原因在于channle被关闭能读出0,所以这个循环在chh被关闭后马上就结束了
      <-chh
   }
}
饭后甜点

昙花逐月在解决上面的问题后,还提出一个问题,怎么利用channel来实现安全退出?,事实上上面的方案就是一个雏形,只是需要稍微修改一下代码,为了方便阅读,我在上述代码的基本盘不变的情况下重新组织一下代码。

func main() {
	data := make(chan int, 100) // 用来存储0-100
	code := make(chan int, 1)   // 相当于ch,写入code则有执行权
	count := make(chan int, 3)  // 相当于chh,用于统计

	for i := 0; i < 3; i++ {
		go func(code chan int, data <-chan int, count chan<- int) {
			for {
				code <- 1 // 获得执行权
				if v, ok := <-data; ok {
					fmt.Println(v)
					<-code //释放执行权
				} else {
					count <- 1 // 计数
					<- code //释放执行权
					return
				}
			}
		}(code, data, count)
	}

	go func(data chan<- int) {
		for i := 0; i < 101; i++ {
			data <- i
		}
		close(data)
	}(data)

	for i := 0; i < 3; {
		i = i + <-count // 统计
	}
}

事实上这种实现和sync.WaitGroup有异曲同工之妙

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/945264.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号