GMP调度模型,不做过多赘述。
那么Golang 已经实现了 M:N 的用户态携程Goroutine,还要必要在 Golang 里实现goroutine池吗?
个人感觉还是必要的,因为在某些情况下还是要限制一下并发量的。
同时就算goroutine的开销低,当对性能要求高,并且goroutine里面执行的操作时间极小时,频繁创建goroutine进行调用也是不可取的。
接下来实现一个控制并发量的pool,但是不保存goroutine。比较适合小项目。
package pool
import (
"fmt"
"time"
)
func NewGoPool(queueMax int, workerCount int) *GoPool {
taskQueue := taskQueue{}
taskQueue.create(queueMax)
slavePool := slavePool{}
slavePool.create(workerCount)
return &GoPool{taskQueue: taskQueue, slavePool: slavePool}
}
type GoPool struct {
taskQueue taskQueue
slavePool slavePool
stop chan struct{}
}
func (gp *GoPool) AddTask(task func(), wait time.Duration) bool {
return gp.taskQueue.addTask(task, wait)
}
func (gp *GoPool) StartRun() {
go func() {
for {
select {
case job := <-gp.taskQueue.tasks:
slave := <-gp.slavePool.slaves
slave.job <- job
case <-gp.stop:
for i := 0; i < cap(gp.slavePool.slaves); i++ {
slave := <-gp.slavePool.slaves
slave.stop <- struct{}{}
<-slave.stop
}
gp.stop <- struct{}{}
return
}
}
}()
}
func (gp *GoPool) Stop() {
gp.stop <- struct{}{}
<-gp.stop
}
type taskQueue struct {
tasks chan func()
}
func (tq *taskQueue) create(max int) {
tq.tasks = make(chan func(), max)
}
func (tq *taskQueue) addTask(task func(), wait time.Duration) bool {
timeout := make(chan bool, 1)
go func() {
time.Sleep(wait)
timeout <- true
}()
select {
case tq.tasks <- task:
return true
case <-timeout:
return false
}
}
type slavePool struct {
slaves chan *slave
}
func (sp *slavePool) create(max int) {
sp.slaves = make(chan *slave, max)
for i := 0; i < cap(sp.slaves); i++ {
sla := slave{parentPool: sp}
sla.job = make(chan func())
sla.stop = make(chan struct{})
sla.start()
}
}
type slave struct {
parentPool *slavePool
job chan func()
stop chan struct{}
}
func (s *slave) start() {
go func() {
for {
// worker free, add it to pool
s.parentPool.slaves <- s
select {
case job := <-s.job:
fmt.Println("do job")
job()
case <-s.stop:
s.stop <- struct{}{}
return
}
}
}()
}
main.go
package main
import (
"fmt"
"simontest/pool"
"time"
)
func main() {
goPool := pool.NewGoPool(2, 1)
goPool.StartRun()
for i := 0; i < 106; i++ {
count := i
res := goPool.AddTask(func() {
fmt.Printf("I am task! Number %d\n")
}, 3*time.Second)
fmt.Printf("I am save! Number %d %v\n", count, res)
}
time.Sleep(1000 * time.Second)
}