如何安全close go 的channel
2023-09-14 08:59:54 时间
golang 在多协程下, 不清楚谁是 sender 谁是 recver 的时候, close(chan) 和 chan<-data 两个都很容易报错
原则:
//1 close 永远和data分离, 不要close 还会发送或接受的 channel //2 close 永远要做好 'close of closed channel' 错误的发生 //3 send (也就是 <- 操作) 永远不要让他出现 'send to a nil channel' 其实也就是第一点
那么close 到底怎么才能更安全,更正确的,解决呢?
单协程 直接close ,太low,我们不考虑,只考虑最复杂环境下的情况.
第一种方法 暴力关闭channel ,只需要增加 recover() 就可以了
第二种方法 通过对变量加锁,或者直接使用sync.Once 来控制 close 只有一次
第三种方法 创建两个chan,一个永远不close,只进行 无限 发送, 单个接收; 一旦接收了 ,就关闭另外一个; 另外一个 返还给外部, 只判断 close的情况,不进行发送和直接close
第四种方法 比较简单,总是 使用select就可以了,
done := make(chan struct{})
// 复杂环境下 并发 进行关闭
select{
case <-done:
default:
close(done)
}
//复杂环境 并发 检测是否关闭
select{
case <-done
//关闭处理
return
default:
//没有关闭的处理
}
经过高并发测试,发现 上面的select 关闭方法, 还是不安全, select不是原子性, 只要异步 数量达到值,就还是会有出现panic的情况;
测试用例:
运行 Test_SafeCloseChan2 几乎是必现的,只不过是 成功 END 出现几次罢了.
func Test_SafeCloseChan2(t *testing.T) { for i := 0; i < 1000; i++ { Test_SafeCloseChan(t) } } func Test_SafeCloseChan(t *testing.T) { done := make(chan bool) begin := time.Now().Add(300 * time.Millisecond) wg := sync.WaitGroup{} for i := 0; i < 1000000; i++ { wg.Add(1) go func(i int) { defer func() { wg.Done() }() //等待开始,一起开始,避免不同时 for { if time.Now().After(begin) { break } time.Sleep(1 * time.Microsecond) } select { case <-done: //case <-time.After(500 * time.Millisecond): // select { // case <-done: // default: // close(done) // t.Log("Success close(done)") // } //case <-time.After(500 * time.Millisecond): // t.Log("Success close(done)") // close(done) default: close(done) t.Log("Success close(done)") } }(i) } wg.Wait() t.Log("=============END=========") }
所以, 还是加 panic 吧
https://zhuanlan.zhihu.com/p/32529039
相关文章
- go递归实现快排
- go富集分析和kegg富集分析的区别_非模式生物怎么做GO富集
- Go-Excelize API源码阅读(十一)—— GetActiveSheetIndex()
- 你有对象类,我有结构体,Go lang1.18入门精炼教程,由白丁入鸿儒,go lang结构体(struct)的使用EP06
- 从.go文本文件到可执行文件
- 如何优雅的通过Shell脚本一键部署GO项目到服务器 |Go主题月
- go-cqhttp 群消息发送失败: 账号可能被风控的几种解决方法
- 「Go工具箱」go语言csrf库的使用方式和实现原理
- 「Go工具箱」web中想做到cookie值安全?securecookie库的使用和实现原理
- go语言学习之reflect
- 用 Go 从零实现日志包 - 第零篇 序言
- 「Go工具箱」一个简单、易用的多错误管理包:go-multierror
- 不背锅运维:Go实现aes加密,并带你手撸一个命令行应用程序
- go手动添加锁=安全
- Go 模块 verifying xxx/go.mod: checksum mismatch 问题处理
- Go-包管理-go mod(二)
- Go语言多个变量同时赋值
- Go语言range关键字:循环迭代切片
- Go 处理yaml类型的配置文件详解编程语言
- Go-连接Redis-学习go-redis包详解编程语言
- Go语言-基本的http请求操作详解编程语言
- Go—go-cache包学习详解编程语言
- 部署Linux下快速部署Go环境(go环境linux)
- 数据库Go语言实现监控Oracle数据库(go监听oracle)
- 给Oracle数据库添加专业技能,Go大发展(go_db_oracle)
- 踏上oracle编程之路,Go(go oracle编程)
- Redis集群搭建Go的艰难之路(redis集群搭建go)