为防止无限制开启gorouting造成的cpu性能浪费以及更严重的内存溢出和程序崩溃,这里加单实现一下协程池
深入理解:http://marcio.io/2015/07/handling-1-million-requests-per-minute-with-golang/
流程图:
代码实现:
package main
import (
"fmt"
"time"
)
//----------------------有关任务的功能-----------
//定义一个任务类型
type Task struct {
f func() error //一个Task里有一个具体的业务,业务名称叫f
}
//创建一个Task任务
func NewTask(arg_f func() error) *Task {
return &Task{f:arg_f}
}
//task执行业务的方法
func (t *Task) Execute() {
err := t.f() //真正执行具体业务
if err != nil {
fmt.Println("任务出错:", err)
}
}
//----------------------有关协程池Pool的功能-----------
//定义协程池
type Pool struct {
//对外的task入口
EntryChannel chan *Task
//内部的task队列
JobsChannel chan *Task
//协程池中worker数量
worker_num int
}
//创建协程池
func NewPool(cap int) *Pool{
return &Pool{
EntryChannel:make(chan *Task),
JobsChannel:make(chan *Task),
worker_num:cap,
}
}
//协程池创建一个worker,让这个worker取队列里拿任务执行
func (p *Pool) startOneWorker(worker_ID int){
//一直永久地从JobsChannel取任务
for task := range p.JobsChannel {
//取到任务后开始执行
task.Execute()
fmt.Println("worker_ID ",worker_ID, " 执行完了一个任务!")
}
}
//启动协程池
func (p *Pool) run() {
//根据worker_num创建worker
for i := 0; i < p.worker_num; i++ {
//启动自定数量的工人,并开始执行任务
go p.startOneWorker(i)
}
//从EntryChannel中取任务,将新任务发给内部队列JobsChannel
for task := range p.EntryChannel{
p.JobsChannel <- task
}
}
func main() {
//1.创建一些任务。这里示例用循环重复一个任务
t := NewTask(func() error {
fmt.Println(time.Now())
return nil
})
//2.创建容量为4的协程池
p := NewPool(4)
task_num := 0
//3.将任务放入协程池外部接口
go func() {
for {
p.EntryChannel <- t
task_num ++
fmt.Println("当前一个执行了 ",task_num," 个任务!")
}
}()
//4.启动pool
p.run()
}