管理动态goroutine池的最佳方法

let*_*4be 5 pool go goroutine

假设我有一个任务提供程序-可读的通道,它可能会或可能不会提供任务(取决于工作量)具体来说,这样一来可能几个小时都没有工作,然后可能突然出现任务

我想让我的goroutine池从1增长到N,其中N是出现工作时的最大并发性,然后自动崩溃到1,在那里goroutine的工作时间超过X秒,以避免内存/ cpu浪费。

我本可以只使用一个固定的池,因为goroutine非常便宜,但是我不喜欢有成千上万个空闲的goroutine,我可能会更好地使用那些资源(应该主要是ram,但仍然可以)

折叠部分相当容易

for {
    timeoutTimer := time.NewTimer(WORKER_ROUTINE_TIMEOUT)

    select {
    case taskContext, isBatchRunning := <-runner.tasksCh:
        if !isBatchRunning {
            log.Print("task provider is closed, quit worker goroutine")
            return
        }

        runner.job.Process(&taskContext)
    case <-timeoutTimer.C:
        return
    }
}
Run Code Online (Sandbox Code Playgroud)

但是我不确定如何使池动态增长,即在哪种条件下产生新的池

此池的优先级是能够对增加的负载做出快速反应并扩展到N(最大并发)goroutines,并能够在工作负载减少时最终崩溃到更合理的数量(最小为1)。

PS:我看到了一个https://github.com/Jeffail/tunny包,但看起来它与当前池大小的自适应缩放没有任何相似之处。我想念什么吗?

谢谢!

小智 2

嗯,我不确定您需要的是游泳池。Goroutines 启动速度很快,您可能不需要一直让它们保持准备状态。

对于此任务,我将使用一个简单的信号量。在 Go 中使用通道实现信号量非常容易。您可以在这里查看我的个人示例。

您只需创建一个具有所需容量的信号量(这将是允许的 goroutine 的最大数量),然后:

  1. 在 goroutine 启动时获取信号量
  2. 在 goroutine 端释放它

就那么简单。并且不用担心是否需要按需启动它们;这确实是过度优化了。