【犯傻经历】虽然用原子加减来限制协程总数,但是协程加上了永不退出的代码

代码大致如下

var coroutineCount int64 = 0
const maxCoroutineCount = 4000
const defaultSleepMs = 100

func Inc(){
    atomic.AddInt64(&coroutineCount, 1)
}

func Dec(){
    atomic.AddInt64(&coroutineCount, -1)
}

func job1(){
    Inc()
    defer Dec()
    //do something
}

func job2(){
    Inc()
    defer Dec()
    //do something
    for {
        coroutineCount := atomic.LoadInt64(&coroutineCount)
        if coroutineCount>=maxCoroutineCount{
            time.Sleep(defaultSleepMs*time.Millisecond)
            continue
        }
        break
    }
    //do something
}

func genJob(){
    for {
        for {
            coroutineCount := atomic.LoadInt64(&coroutineCount)
            if coroutineCount>=maxCoroutineCount{
                time.Sleep(defaultSleepMs*time.Millisecond)
                continue
            }
            break
        }
        go job1()
        go job2()
    }
}

这样的代码一开始很好,跑几分钟后莫名的停止了。
导致问题的代码是 job2()中的那个循环检查:

    for {
        coroutineCount := atomic.LoadInt64(&coroutineCount)
        if coroutineCount>=maxCoroutineCount{
            time.Sleep(defaultSleepMs*time.Millisecond)
            continue
        }
        break
    }

原因是:某一瞬间如果协程的数量大于等于maxCoroutineCount的时候,这个循环几乎不会结束。

教训:

  • 产生任务的生产者,对协程数量做检查,并进行等待,是对的;但是执行具体任务的协程一定要在确定的时间内结束,任何可能陷入永远循环的地方都要避免;
  • 如果一定要等待,设定一个超时时间,不能无限等待;
  • 最好加上监控计数,当某个位置卡死的时候,能够快速定位到。
原文地址:https://www.cnblogs.com/ahfuzhang/p/13080918.html