zoukankan      html  css  js  c++  java
  • 遇到一个golang time.Tick的坑

    time.Tick的巨坑

    go版本

    >go version
    go version go1.15.4 windows/amd64
    

    api文档

    func Tick(d Duration) <-chan Time
        Tick is a convenience wrapper for NewTicker providing access to the ticking channel only. 
        While Tick is useful for clients that have no need to shut down the Ticker, be aware that 
        without a way to shut it down the underlying Ticker cannot be recovered by the garbage 
        collector; it "leaks". Unlike NewTicker, Tick will return nil if d <= 0.
    

    问题源码

    • 现象: etcd关闭expired后循环不再进入
    	for range time.Tick(time.Duration(ticker) * time.Second) {
    		now := time.Now().Unix()
    		lvs := make(map[string]*LeaseResult)
    		leaseIndex.Range(func(key, value interface{}) bool {
    			if v := value.(*LeaseResult); v.Exp <= now {
    				lvs[key.(string)] = v
    			}
    			return true
    		})
    		var err error
    		for lk, lv := range lvs {
    			if err = client.SendKeepAlive(lv.Id); err != nil {
    				if LeaseTimeoutError(err) {
    					// 如果stream关闭(etcd连接断开)重连后相关的lease可能过期,届时会报错
    					if DEBUG {
    						base.DefaultLogger.Debugf("etcd lease reset (send): lid=%x, key=%v", lv.Id, lk)
    					}
    					if id, ttl, err := client.Grant(expire); err == nil {
    						if err = client.Put(lk, lv.Val, id); err == nil {
    							lv.Id = id
    							lv.Exp = Exp(now, ttl)
    							leaseIndex.Store(lk, lv)
    						}
    					}
    				} else {
    					return err
    				}
    			}
    			if DEBUG {
    				base.DefaultLogger.Debugf("etcd lease send: lid=%x, key=%v, err=%v", lv.Id, lk, err)
    			}
    		}
    	}
    

    解决源码

        // FIXBUG: 必须注意, 此处不能使用"for range ticker"的写法,在etcd关闭expire+后循环不再执行! 相当奇怪!
    	for {
    		now := time.Now().Unix()
    		lvs := make(map[string]*LeaseResult)
    		leaseIndex.Range(func(key, value interface{}) bool {
    			if v := value.(*LeaseResult); v.Exp <= now {
    				lvs[key.(string)] = v
    			}
    			return true
    		})
    		var err error
    		for lk, lv := range lvs {
    			if err = client.SendKeepAlive(lv.Id); err != nil {
    				if LeaseTimeoutError(err) {
    					// 如果stream关闭(etcd连接断开)重连后相关的lease可能过期,届时会报错
    					if DEBUG {
    						base.DefaultLogger.Debugf("etcd lease reset (send): lid=%x, key=%v", lv.Id, lk)
    					}
    					if id, ttl, err := client.Grant(expire); err == nil {
    						if err = client.Put(lk, lv.Val, id); err == nil {
    							lv.Id = id
    							lv.Exp = Exp(now, ttl)
    							leaseIndex.Store(lk, lv)
    						}
    					}
    				} else {
    					return err
    				}
    			}
    			if DEBUG {
    				base.DefaultLogger.Debugf("etcd lease send: lid=%x, key=%v, err=%v", lv.Id, lk, err)
    			}
    		}
    		time.Sleep(time.Duration(ticker) * time.Second)
    	}
    

    原因归结

    1. time.Tick()生成某个channel
    2. 内部往channel push
    3. 循环从channel pop
    4. 一旦内部失败,则循环永久阻塞...
  • 相关阅读:
    HDU 4565 So Easy!(数学+矩阵快速幂)(2013 ACM-ICPC长沙赛区全国邀请赛)
    HDU 4568 Hunter(最短路径+DP)(2013 ACM-ICPC长沙赛区全国邀请赛)
    URAL 1664 Pipeline Transportation(平面图最大流)
    HDU 1250 Hat's Fibonacci(高精度)
    HDU 1042 N!(高精度乘)
    算法模板の计算几何
    算法模板の数据结构
    算法模板の数学&数论
    算法模板之图论
    HDU 3260/POJ 3827 Facer is learning to swim(DP+搜索)(2009 Asia Ningbo Regional)
  • 原文地址:https://www.cnblogs.com/zolo/p/14439969.html
Copyright © 2011-2022 走看看