Go channel关闭方法

news/2025/1/23 23:21:23/

channel关闭原则

1、不能在消费端关闭channel(基础原则,单生产者或多生产者均不能在消费端关闭);

2、多个生产者时,不能对channel执行关闭;

3、只有在唯一或最后唯一剩下的生产者协程中关闭channel,之后通知消费者
   没有值可以读,才能确保向一个已经关闭的channel中不再发送数据;

暴力关闭channel

强行在消费端或多个生产者端关闭channel会产生pannic,可以使用recover机制接收异常避免崩溃;

生产端:
func SafeSend(ch chan T, value T) (closed bool) {defer func() {if recover() != nil {// The return result can be altered // in a defer function call.closed = true}}()ch <- value // panic if ch is closedreturn false // <=> closed = false; return
}消费端:
func SafeClose(ch chan T) (justClosed bool) {defer func() {if recover() != nil {justClosed = false}}()// assume ch != nil here.close(ch) // panic if ch is closedreturn true // <=> justClosed = true; return
}

不同生产者消费者关闭channel情况

1.单生产者单(多)消费者

直接在生产者端close();

2.多生产者单消费者

不能在生产者端直接close(),需要新建一个信号channel,通知发送端停止发送数据;channel在没有go协程引用时会自动关闭,不用显式关闭;

package mainimport ("time""math/rand""sync""log"
)func main() {rand.Seed(time.Now().UnixNano())log.SetFlags(0)// ...const MaxRandomNumber = 100000const NumSenders = 1000wgReceivers := sync.WaitGroup{}wgReceivers.Add(1)// ...dataCh := make(chan int, 100)stopCh := make(chan struct{})// stopCh is an additional signal channel.// Its sender is the receiver of channel dataCh.// Its reveivers are the senders of channel dataCh.// stopCh为添加的信号channel,它的数据来源是dataCh的接受端发出的数据,它的数据    // 是在dataCh的生产端进行消费;// sendersfor i := 0; i < NumSenders; i++ {go func() {for {// The first select here is to try to exit the goroutine// as early as possible. In fact, it is not essential// for this example, so it can be omitted.select {case <- stopCh:returndefault:}// Even if stopCh is closed, the first branch in the// second select may be still not selected for some// loops if the send to dataCh is also unblocked.// But this is acceptable, so the first select// can be omitted.// ?对于某些loop,dataCh的生产端塞入数据,即使stopCh已经关闭,第二个select的           // 第一个分支仍然可能不能被选择(select如果多个条件同时满足条件,会随机选择);select {case <- stopCh:returncase dataCh <- rand.Intn(MaxRandomNumber):}}}()}// the receivergo func() {defer wgReceivers.Done()for value := range dataCh {if value == MaxRandomNumber-1 {// The receiver of the dataCh channel is// also the sender of the stopCh cahnnel.// It is safe to close the stop channel here.close(stopCh)return}log.Println(value)}}()// ...wgReceivers.Wait()
}
3.多生产者多消费者

不能让接受端或发送端关闭channel,甚至都不能让接受者关闭一个退出信号来通知生产者停止生产,因为多消费者会导致接受者多次执行close(),相当于多个生产端关闭channel,违反了channel关闭原则,但可以引入一个额外的协调者来关闭附加的退出信号channel。

package mainimport ("time""math/rand""sync""log""strconv"
)func main() {rand.Seed(time.Now().UnixNano())log.SetFlags(0)// ...const MaxRandomNumber = 100000const NumReceivers = 10const NumSenders = 1000wgReceivers := sync.WaitGroup{}wgReceivers.Add(NumReceivers)// ...dataCh := make(chan int, 100)stopCh := make(chan struct{})// stopCh is an additional signal channel.// Its sender is the moderator goroutine shown below.// Its reveivers are all senders and receivers of dataCh.toStop := make(chan string, 1)// The channel toStop is used to notify the moderator// to close the additional signal channel (stopCh).// Its senders are any senders and receivers of dataCh.// Its reveiver is the moderator goroutine shown below.var stoppedBy string// moderatorgo func() {stoppedBy = <- toStopclose(stopCh)}()// sendersfor i := 0; i < NumSenders; i++ {go func(id string) {for {value := rand.Intn(MaxRandomNumber)if value == 0 {// Here, a trick is used to notify the moderator// to close the additional signal channel.select {case toStop <- "sender#" + id:default:}return}// The first select here is to try to exit the goroutine// as early as possible. This select blocks with one// receive operation case and one default branches will// be optimized as a try-receive operation by the// official Go compiler.select {case <- stopCh:returndefault:}// Even if stopCh is closed, the first branch in the// second select may be still not selected for some// loops (and for ever in theory) if the send to// dataCh is also unblocked.// This is why the first select block is needed.select {case <- stopCh:returncase dataCh <- value:}}}(strconv.Itoa(i))}// receiversfor i := 0; i < NumReceivers; i++ {go func(id string) {defer wgReceivers.Done()for {// Same as the sender goroutine, the first select here// is to try to exit the goroutine as early as possible.select {case <- stopCh:returndefault:}// Even if stopCh is closed, the first branch in the// second select may be still not selected for some// loops (and for ever in theory) if the receive from// dataCh is also unblocked.// This is why the first select block is needed.select {case <- stopCh:returncase value := <-dataCh:if value == MaxRandomNumber-1 {// The same trick is used to notify// the moderator to close the// additional signal channel.select {case toStop <- "receiver#" + id:default:}return}log.Println(value)}}}(strconv.Itoa(i))}// ...wgReceivers.Wait()log.Println("stopped by", stoppedBy)
}
Context结束多个协程
package mainimport ("context""fmt""sync""time"
)func worker(ctx context.Context, id int, wg *sync.WaitGroup) {defer wg.Done()for {select {case <-ctx.Done():fmt.Printf("Worker %d canceled\n", id)returndefault:// 执行协程的工作任务fmt.Printf("Worker %d working\n", id)time.Sleep(time.Second)}}
}func main() {ctx, cancel := context.WithCancel(context.Background())var wg sync.WaitGroup// 启动多个协程for i := 1; i <= 5; i++ {wg.Add(1)go worker(ctx, i, &wg)}// 主程序等待一段时间后取消所有协程time.Sleep(time.Second * 3)cancel()// 等待所有协程完成wg.Wait()fmt.Println("Main program finished")
}


http://www.ppmy.cn/news/1565609.html

相关文章

web前端第六次作业---制作网页页面

制作网页页面 代码: <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><s…

AR智慧点巡检系统探究和技术方案设计

一、项目背景 随着工业生产规模的不断扩大和设备复杂度的提升&#xff0c;传统的人工点巡检方式效率低下、易出错&#xff0c;难以满足现代化企业对设备运行可靠性和安全性的要求。AR&#xff08;增强现实&#xff09;技术的发展为点巡检工作带来了新的解决方案&#xff0c;通…

2025美赛倒计时,数学建模五类模型40+常用算法及算法手册汇总

数学建模美赛倒计时&#xff0c;对于第一次参加竞赛且没有相关基础知识的同学来讲&#xff0c;掌握数学建模常用经典的模型算法知识&#xff0c;并熟练使用相关软件进行建模是关键。本文将介绍一些常用的模型算法&#xff0c;以及软件操作教程。 数学建模常用模型包括&#xf…

5. 马科维茨资产组合模型+政策意图AI金融智能体(Qwen-Max)增强方案(理论+Python实战)

目录 0. 承前1. AI金融智能体1.1 What is AI金融智能体1.2 Why is AI金融智能体1.3 How to AI金融智能体 2. 数据要素&计算流程2.1 参数集设置2.2 数据获取&预处理2.3 收益率计算2.4 因子构建与预期收益率计算2.5 协方差矩阵计算2.6 投资组合优化2.7 持仓筛选2.8 AI金融…

深度学习实战:使用卷积神经网络(CNN)进行图像分类

在当今的机器学习领域&#xff0c;深度学习&#xff0c;尤其是卷积神经网络&#xff08;CNN&#xff09;&#xff0c;已经在图像分类、物体检测、自然语言处理等领域取得了巨大的成功。本文将通过一个实际的例子&#xff0c;展示如何使用TensorFlow和Keras库构建一个卷积神经网…

IOS 安全机制拦截 window.open

摘要 在ios环境&#xff0c;在某些情况下执行window.open不生效 一、window.open window.open(url, target, windowFeatures) 1. url&#xff1a;「可选参数」&#xff0c;表示你要加载的资源URL或路径&#xff0c;如果不传&#xff0c;则打开一个url地址为about:blank的空…

线上突发:MySQL 自增 ID 用完,怎么办?

线上突发&#xff1a;MySQL 自增 ID 用完&#xff0c;怎么办&#xff1f; 1. 问题背景2. 场景复现3. 自增id用完怎么办&#xff1f;4. 总结 1. 问题背景 最近&#xff0c;我们在数据库巡检的时候发现了一个问题&#xff1a;线上的地址表自增主键用的是int类型。随着业务越做越…

.Net Core微服务入门全纪录(四)——Ocelot-API网关(上)

系列文章目录 1、.Net Core微服务入门系列&#xff08;一&#xff09;——项目搭建 2、.Net Core微服务入门全纪录&#xff08;二&#xff09;——Consul-服务注册与发现&#xff08;上&#xff09; 3、.Net Core微服务入门全纪录&#xff08;三&#xff09;——Consul-服务注…