管理动态goroutine池的最佳方法

huangapple go评论95阅读模式
英文:

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数量),然后:

  1. 在goroutine启动时获取信号量
  2. 在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:

  1. Acqiure the semaphore on goroutine start
  2. 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.

huangapple
  • 本文由 发表于 2015年8月26日 21:21:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/32227857.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定