我会反过来做。我不会生成许多 goroutine(仍然需要大量内存)并使用通道来阻止它们,而是将工作程序建模为 goroutine 并使用通道来分配工作。像这样的东西:
package main
import (
"fmt"
"sync"
)
type Task string
func worker(tasks <-chan Task, quit <-chan bool, wg *sync.WaitGroup) {
defer wg.Done()
for {
select {
case task, ok := <-tasks:
if !ok {
return
}
fmt.Println("processing task", task)
case <-quit:
return
}
}
}
func main() {
tasks := make(chan Task, 128)
quit := make(chan bool)
var wg sync.WaitGroup
// spawn 5 workers
for i := 0; i < 5; i++ {
wg.Add(1)
go worker(tasks, quit, &wg)
}
// distribute some tasks
tasks <- Task("foo")
tasks <- Task("bar")
// remove two workers
quit <- true
quit <- true
// add three more workers
for i := 0; i < 3; i++ {
wg.Add(1)
go worker(tasks, quit, &wg)
}
// distribute more tasks
for i := 0; i < 20; i++ {
tasks <- Task(fmt.Sprintf("additional_%d", i+1))
}
// end of tasks. the workers should quit afterwards
close(tasks)
// use "close(quit)", if you do not want to wait for the remaining tasks
// wait for all workers to shut down properly
wg.Wait()
}
使用一些方便的方法创建一个单独的 WorkerPool 类型可能是个好主意。此外,使用还包含用于指示任务已成功执行type Task string
的通道的结构不是很常见的。done
编辑:我玩了更多,想出了以下内容:http ://play.golang.org/p/VlEirPRk8V 。它基本上是相同的示例,但具有更好的 API。