协程池
- 通常会使用可以指定启动的goroutine数量–worker pool模式,控制goroutine的数量,防止goroutine泄漏和暴涨。
简易worker pool
- 使用goroutine和channel实现一个计算int64随机数各位数和的程序。
- 开启一个goroutine生成int64类型的随机数,发送到jobChan
- 开启24个goroutine从jobChan中取出随机数计算各位数的和,将结果发送到resultChan
- 主goroutine从resultChan取出结果并打印到终端输出
var jobChan chan int64
var resultChan chan int64
func main() {
jobChan = make(chan int64, 100)
resultChan = make(chan int64, 50)
go proInt(jobChan)
for i := 1; i <= 24; i++ { //worker pool
go spendInt(jobChan, resultChan)
}
for x := range resultChan {
time.Sleep(time.Second)
x = <-resultChan
fmt.Println(x)
}
}
//生产者 生成int64类型的随机数,发送到jobChan
func proInt(jobChan chan<- int64) {
for {
jobChan <- rand.Int63()
}
}
//消费者 从jobChan中取出随机数计算各位数的和,将结果发送到resultChan
func spendInt(jobChan <-chan int64, resultChan chan<- int64) {
for {
job := <-jobChan
var sum = int64(0)
for job > 0 {
i := job % 10
job = job / 10
sum += i
}
resultChan <- sum
}
}
协程池的设计思路
//Task 定义任务对象
type Task struct {
f func() error //任务要具体执行的业务 叫f
}
//WorkerPool 定义协程池对象
type WorkerPool struct {
JobChannel chan *Task
EntryChannel chan *Task
MaxNum int
}
//NewTask 创建一个任务实例
func NewTask(fun func() error) (task *Task) {
task = &Task{
f: fun,
}
return
}
//Execute 执行任务实体中的方法
func (t *Task) Execute() {
t.f() //调用任务中已经绑定的方法
}
//NewPool 创建一个协程池实例
func NewPool(maxNum int) (pool *WorkerPool) {
pool = &WorkerPool{
JobChannel: make(chan *Task),
EntryChannel: make(chan *Task),
MaxNum: maxNum,
}
return
}
//Worker 协程池创建一个worker让worker去执行任务
func (w *WorkerPool) Worker(workID int) {
for task := range w.JobChannel { //永久的从jobchannel中拿取任务去执行
task.Execute()
fmt.Println("worker ID:", workID, "has execute a task")
}
}
func (w *WorkerPool) run() {
//1.根据maxNum来创建对应数据的worker来工作
for i := 0; i < w.MaxNum; i++ {
go w.Worker(i)
}
//2.从EntryChannel中取任务,将任务发送给jobchannel
for tast := range w.EntryChannel {
w.JobChannel <- tast
}
}
//testTask 测试使用 模拟具体任务
func testTask() error {
fmt.Println(time.Now().Second())
return nil
}
func main() {
//1.创建一些具体任务
t := NewTask(testTask)
//2.创建一个workPool实例
p := NewPool(6)
//3.将创建的任务流到entryChannel中
go func() {
for { //不断的将任务给写道接口通道中
p.EntryChannel <- t
}
}()
//4.启动pool去执行
p.run()
}