关于go语言,使用channel和timer定时执行以及优雅遇到的坑

 问题描述:

1、我在主进程中,通过go启动了两个协程来处理labor的运行。主进程定位为g0,两个协程分别定义为g1和g2

2、同时在主进程中,通过运行 runConfigChangedMonitor 来定时监控配置文件是否发生变化,如果发生变化就退出

3、退出方法为Shutdown(),在该方法内部,调用labor的Shutdown()来关闭labor的运行,之后通过向chan bool channel设置一个

bool值来尝试退出

4、但是在Shutdown()方法中,

g1和g2的Shutdown()能顺利的执行

但是g0的如下这条语句的执行被hang住了

o.mQuit <- true

 

原因是:

1、默认初始化时使用的chan bool是无缓存channel

2、当在主协程g0内部向一个无缓存channel设置值的时候,默认会被hang住,除非本协程内部能从channel消费数据。但是由于设置和和消费都是同一个协程,所以无缓存情况下,会出现hang的情况

3、在g0设置g1和g2的时候,都没有问题(即便g1和g2协程内部也是定义的无缓存bool channel,但是设置和消费是两个不同的协程)

 

解决方案:

1、要么设置的时候,采用go的匿名方法,通过go 执行匿名方法来实现

2、要么将bool channel 初始化为带缓存的channel也可以解决

 

主要代码如下:

type LaborManager struct {
    mLaborConfs map[string]*LaborConf
    mConfigMD5  string
    mLabors     map[string]ILabor
    mTjApi      IDataaccess
    mFileReader info_reader.IFileReader
    mQuit       chan bool
    mTicker     *time.Ticker
    mLogger     tj.Logger
}

func NewLaborManager(tjApi IDataaccess, fileReader info_reader.IFileReader) *LaborManager {
    fileMonitorDuration, _ := time.ParseDuration("10s")
    return &LaborManager{
        mLaborConfs: map[string]*LaborConf{},
        mFileReader: fileReader,
        mTjApi:      tjApi,
        mLabors:     map[string]ILabor{},
        // 如果初始化成一个不带缓存的bool chan,会出现Shutdown方法的向channel设置true的操作会出现hang的情况
        //mQuit:       make(chan bool),
        // 这里初始化一个带1个数据的缓存的bool channel来避免在相同的进程内部操作chan出现hang
        mQuit:       make(chan bool, 1),
        mTicker:     time.NewTicker(fileMonitorDuration),
        mLogger:     tj.GetLogger("labor_manager"),
    }
}

func (o *LaborManager) Shutdown() {
    for _, labor := range o.mLabors {
        labor.Shutdown()
        o.mLogger.Infof("labor done")
    }

    /*
    当bool channel初始化的时候就是带缓存的,直接向chan中设置并不会hang
    */
    o.mQuit <- true
    o.mLogger.Infof("quit is setted to true")
    /*
       如果bool channel为无缓存channel,使用这段代码可是避免hang住
       go func() {
           o.mQuit <- true
           o.mLogger.Infof("quit is setted to true")
       }()
    */
}


func (o *LaborManager) runConfigChangedMonitor() {
    for {
        select {
        case <-o.mQuit:
            o.mLogger.Infof("config monitor has been exited")
            o.mLogger.Flush()
            o.mTicker.Stop()
            return
        case <-o.mTicker.C:
            o.mLogger.Infof("config monitor begin...")
            if o.IsConfigChanged() {
                o.mLogger.Infof("config changed, exiting...")
                o.Shutdown()
            }
        }
    }
}

 

程序输出结果:


INFO labor_manager 2018/11/14 16:04:13.796465 labor_manager.go:171: config monitor begin...
INFO labor_manager 2018/11/14 16:04:23.796459 labor_manager.go:171: config monitor begin...
INFO labor_manager 2018/11/14 16:04:33.796429 labor_manager.go:171: config monitor begin...
INFO labor_manager 2018/11/14 16:04:43.796482 labor_manager.go:171: config monitor begin...
INFO labor_manager 2018/11/14 16:04:43.796621 labor_manager.go:173: config changed, exiting...
INFO labor_manager 2018/11/14 16:04:43.796638 labor_manager.go:99: labor done
INFO labor_manager 2018/11/14 16:04:43.796656 labor_manager.go:99: labor done
INFO labor_manager 2018/11/14 16:04:43.796660 labor_manager.go:103: quit is setted to true
INFO labor_manager 2018/11/14 16:04:43.796666 labor_manager.go:166: config monitor has been exited

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值