英文:
The best way to manage a dynamic goroutine pool
问题
假设我有一个任务提供者 - 可读通道,它可能会提供任务(取决于工作负载)
具体来说,可能几个小时都没有任务,然后突然出现大量任务。
我想让我的goroutine池从1增长到N,其中N是最大并发数,当有任务出现时,自动缩小到1,当一个goroutine长时间没有任务时,以避免内存/ CPU 浪费。
我本可以使用一个固定的池,因为goroutine非常廉价,但我不喜欢有成千上万个空闲的goroutine,我可能有更好的用途(主要是内存,但仍然需要考虑)。
缩小的部分相对容易实现
for {
timeoutTimer := time.NewTimer(WORKER_ROUTINE_TIMEOUT)
select {
case taskContext, isBatchRunning := <-runner.tasksCh:
if !isBatchRunning {
log.Print("任务提供者已关闭,退出工作goroutine")
return
}
runner.job.Process(&taskContext)
case <-timeoutTimer.C:
return
}
}
但是我不确定如何动态地扩展池,即在什么条件下生成新的goroutine。
这个池的优先级是能够快速响应增加的负载并扩展到N(最大并发数)个goroutine,同时能够在工作负载减少时最终缩小到一个更合理的数量(至少为1)。
附注:我看到了一个名为https://github.com/Jeffail/tunny的包,但它似乎没有类似于自适应调整当前池大小的功能。我有什么遗漏吗?
谢谢!
英文:
Let's say I have a task provider - readable channel, it may or may not provide a task(depends on work load)
Specific is so that there may be no work for hours and then there can be a sudden bump in tasks
I want to let my goroutine pool grow from 1 to N where N is the max concurrency when work appears and then automatically collapse down to 1 where there was no work for a goroutine for longer than X sec to avoid memory/cpu waste.
I could have used just a fixed pool, as goroutines are dirt cheap, but I don't like an idea of having thousands of idling goroutines I may have a better use for those resources(should be mostly ram but still)
Collapsing part is rather easy
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
}
}
But i'm not sure how to make pool grow dynamically, i.e. on which condition spawn a new one
A priority for this pool is being able to react fast on increased loading and expand up to N(max concurrency) goroutines, with ability to eventually collapse to a more reasonable numbers(1 at min) when work load decreased
P.S. I saw a https://github.com/Jeffail/tunny package, but it looks like it does not have anything similar to adaptive scaling of the current pool size. Am I missing something?
Thanks!
答案1
得分: 2
嗯,我不确定你是否需要一个池。Goroutines的启动速度很快,你可能不需要一直保持它们准备就绪。
对于这个任务,我会使用一个简单的信号量。在Go语言中使用通道实现信号量非常简单。你可以在这里看到我的个人示例这里。
你只需要创建一个具有所需容量的信号量(这将是允许的最大goroutine数量),然后:
- 在goroutine启动时获取信号量
- 在goroutine结束时释放信号量
就这么简单。
不要担心需要按需启动它们;这样做实际上是过度优化的。
英文:
Well, I'm not sure that a pool is what you need. Goroutines are fast to launch, and you probably don't need to keep them ready all the time.
For this task I would use a simple semaphore. It's really easy to implement a semaphore in Go using channels. You can see my personal example here.
You just create a semaphore with a desired capacity (which will be your maximum number of allowed goroutines) and then:
- Acqiure the semaphore on goroutine start
- Release it on goroutine end
Simple as that.
And don't worry about the need to launch them on demand; it is really over-optimizing.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论