如何优雅的关闭golang的channel
How to Gracefully Close Channels,这篇博客讲了如何优雅的关闭channel的技巧,好好研读,收获良多。
众所周知,在golang中,关闭或者向已关闭的channel发送数据都会引发panic。
谨遵优雅关闭channel的原则
- 不要在接受一端关闭channel
- 不要在有多个并发的senders中关闭channel。反过来说,如果只有一个协程充当sender,那么我们可以在这个sender协程内关闭掉channel。
一个简单的方法
- SafeClose
type MyChannel struct {
C chan T
closed bool
mutex sync.Mutex
}
func NewMyChannel() *MyChannel {
return &MyChannel{C: make(chan T)}
}
func (mc *MyChannel) SafeClose() {
mc.mutex.Lock()
defer mc.mutex.Unlock()
if !mc.closed {
close(mc.C)
mc.closed = true
}
}
func (mc *MyChannel) IsClosed() bool {
mc.mutex.Lock()
defer mc.mutex.Unlock()
return mc.closed
}
- SafeSend
func SafeSend(ch chan T, value T) (closed bool) {
defer func() {
if recover() != nil {
closed = true
}
}()
ch <- value // panic if ch is closed
return false // <=> closed = false; return
}
- [x] 那边英文博客有一句话
One drawback of the above SafeSend function is that its calls can't be used as send operations which follow the case keyword in select blocks.
这里指的是SafeSend方法不能用在select...case...的case接受操作中,即
select {
case <- SafeSend(ch, 1)
}
因为case后面需要一个channel。
优雅关闭channel的设计
- 多个receivers,一个sender的情况。
package main
import (
"time"
"math/rand"
"sync"
"log"
)
func main() {
rand.Seed(time.Now().UnixNano())
log.SetFlags(0)
// ...
const MaxRandomNumber = 100000
const NumReceivers = 100
wgReceivers := sync.WaitGroup{}
wgReceivers.Add(NumReceivers)
// ...
dataCh := make(chan int, 100)
// the sender
go func() {
for {
if value := rand.Intn(MaxRandomNumber); value == 0 {
// The only sender can close the channel safely.
close(dataCh)
return
} else {
dataCh <- value
}
}
}()
// receivers
for i := 0; i < NumReceivers; i++ {
go func() {
defer wgReceivers.Done()
// Receive values until dataCh is closed and
// the value buffer queue of dataCh is empty.
for value := range dataCh {
log.Println(value)
}
}()
}
wgReceivers.Wait()
}
- 一个receiver,多个senders的情况。
package main
import (
"time"
"math/rand"
"sync"
"log"
)
func main() {
rand.Seed(time.Now().UnixNano())
log.SetFlags(0)
// ...
const MaxRandomNumber = 100000
const NumSenders = 1000
wgReceivers := 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 receivers are the senders of channel dataCh.
// senders
for i := 0; i < NumSenders; i++ {
go func() {
for {
// The try-receive operation is to try to exit
// the goroutine as early as possible. For this
// specified example, it is not essential.
select {
case <- stopCh:
return
default:
}
// 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 for this example, so the
// first select block above can be omitted.
select {
case <- stopCh:
return
case dataCh <- rand.Intn(MaxRandomNumber):
}
}
}()
}
// the receiver
go 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 channel.
// It is safe to close the stop channel here.
close(stopCh)
return
}
log.Println(value)
}
}()
// ...
wgReceivers.Wait()
}
- 多个receivers和多个senders
package main
import (
"time"
"math/rand"
"sync"
"log"
"strconv"
)
func main() {
rand.Seed(time.Now().UnixNano())
log.SetFlags(0)
// ...
const MaxRandomNumber = 100000
const NumReceivers = 10
const NumSenders = 1000
wgReceivers := 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 receivers 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 receiver is the moderator goroutine shown below.
// It must be a buffered channel.
var stoppedBy string
// moderator
go func() {
stoppedBy = <-toStop
close(stopCh)
}()
// senders
for i := 0; i < NumSenders; i++ {
go func(id string) {
for {
value := rand.Intn(MaxRandomNumber)
if value == 0 {
// Here, the try-send operation is to notify the
// moderator to close the additional signal channel.
select {
case toStop <- "sender#" + id:
default:
}
return
}
// The try-receive operation here is to try to exit the
// sender goroutine as early as possible. Try-receive
// try-send select blocks are specially optimized by the
// standard Go compiler, so they are very efficient.
select {
case <- stopCh:
return
default:
}
// Even if stopCh is closed, the first branch in this
// select block may be still not selected for some
// loops (and for ever in theory) if the send to dataCh
// is also non-blocking. If this is not acceptable,
// then the above try-receive operation is essential.
select {
case <- stopCh:
return
case dataCh <- value:
}
}
}(strconv.Itoa(i))
}
// receivers
for i := 0; i < NumReceivers; i++ {
go func(id string) {
defer wgReceivers.Done()
for {
// Same as the sender goroutine, the try-receive
// operation here is to try to exit the receiver
// goroutine as early as possible.
select {
case <- stopCh:
return
default:
}
// Even if stopCh is closed, the first branch in this
// select block may be still not selected for some
// loops (and for ever in theory) if the receive from
// dataCh is also non-blocking. If this is not acceptable,
// then the above try-receive operation is essential.
select {
case <- stopCh:
return
case 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)
}
如何优雅的关闭golang的channel的更多相关文章
- 如何优雅的关闭Golang Channel?
Channel关闭原则 不要在消费端关闭channel,不要在有多个并行的生产者时对channel执行关闭操作. 也就是说应该只在[唯一的或者最后唯一剩下]的生产者协程中关闭channel,来通知消费 ...
- golang的channel实现
golang的channel实现位于src/runtime/chan.go文件.golang中的channel对应的结构是: // Invariants: // At least one of c.s ...
- golang的Channel
golang的Channel Channel 是 golang 一个非常重要的概念,如果你是刚开始使用 golang 的开发者,你可能还没有真正接触这一概念,本篇我们将分析 golang 的Chann ...
- 如何优雅的关闭Java线程池
面试中经常会问到,创建一个线程池需要哪些参数啊,线程池的工作原理啊,却很少会问到线程池如何安全关闭的. 也正是因为大家不是很关注这块,即便是工作三四年的人,也会有因为线程池关闭不合理,导致应用无法正常 ...
- Effective java 系列之更优雅的关闭资源-try-with-resources
背景: 在Java编程过程中,如果打开了外部资源(文件.数据库连接.网络连接等),我们必须在这些外部资源使用完毕后,手动关闭它们.因为外部资源不由JVM管理,无法享用JVM的垃圾回收机制,如果我们不在 ...
- 更优雅地关闭资源 - try-with-resource及其异常抑制
原文:https://www.cnblogs.com/itZhy/p/7636615.html 一.背景 我们知道,在Java编程过程中,如果打开了外部资源(文件.数据库连接.网络连接等),我们必须在 ...
- Java进阶知识点:更优雅地关闭资源 - try-with-resource
一.背景 我们知道,在Java编程过程中,如果打开了外部资源(文件.数据库连接.网络连接等),我们必须在这些外部资源使用完毕后,手动关闭它们.因为外部资源不由JVM管理,无法享用JVM的垃圾回收机制, ...
- Java进阶知识点3:更优雅地关闭资源 - try-with-resource及其异常抑制
一.背景 我们知道,在Java编程过程中,如果打开了外部资源(文件.数据库连接.网络连接等),我们必须在这些外部资源使用完毕后,手动关闭它们.因为外部资源不由JVM管理,无法享用JVM的垃圾回收机制, ...
- 如何优雅地关闭一个socket
最近在windows编程时需要考虑到“如何优雅地关闭一个socket”,查阅了一些资料,现将查到的相关资料做个汇编,希望能对后来者有所帮助(比较懒,所以英文资料没有翻译:-)) 1. 关闭Socket ...
随机推荐
- 对js原型及构造函数的相关理解
一.js中的原型创建(声明)一个函数,浏览器在内存中会创建一个对象.每个函数都默认会有一个属性prototype指向了这个对象,就是说prototype的属性的值就是这个对象.此对象就是该函数的原型对 ...
- 使用electron开发指静脉客户端遇到的问题总结
使用electron 使用nodejs 的ffi模块调用dll文件 总结1.electron 与nodejs版本不需要一致,甚至nodejs版本应该高于electron的node版本2.要安装 Vis ...
- Web缓存和静态化
Web缓存和静态化 目录 Web缓存基础... 1 什么是Web缓存... 1 Web缓存的类型... 1 为何要使用Web缓存... 1 重验证... 1 更新... 2 浏览器缓存... 2 工作 ...
- tp5
tp5.1创建模块 把build.php放在应用目录下面, 然后打开cmd, cd../../ cd phpstudy/www/tp5 php think build tp5.1控制器 return ...
- 第46章:MongoDB-监控应用状态
① MongoDB监控 1 db.serverStatus() 查看实例运行状态(内存使用.锁.用户连接等信息) 通过比对前后快照进行性能分析 "connections" # 当 ...
- uc/osⅡ/Ⅲ
1.关于任务堆栈时#if在main()中的用法: #if ... #else#endif//与#if对应作为一个编译“开关”,比如#if(条件满足) 执行代码1 #else 执行代码2 #endif ...
- 接口测试工具之Postman笔记
根据学习内容对Postman进行的个人总结,对于Postman说明.安装方法等说明性文字就不赘述了. 下面是页面中元素的和输入说明: New collection:集合可以把同一平台.系统,或功能的接 ...
- VS code的疑惑之处
作为一个新手,我充满疑惑 eg:下载了git但无法使用匹配 然后在各位博主的详细解释下知道 VS code更新后的git.path被格式化了: so需要进行路径覆盖. 在这之后我的终端依旧出现问题 ...
- eclipse里报:An internal error occurred during: "Building workspace". Java heap space(内存溢出)
当在eclipse中的web工程中增加了extjs4,出现An internal error occurred during: "Building workspace".Java ...
- Windows 10 IoT Core 17101 for Insider 版本更新
除夕夜,微软发布了Windows 10 IoT Core 17101 for Insider 版本更新,本次更新只修正了一些Bug,没有发布新的特性. 已知的问题: F5 driver deploym ...