Go语言协程池的实现示例

Written by

in

文章目录
  • 协程池简单理解就是有一个池子一样的东西,里面装有固定数量的goroutine,当有一个任务到来的时候,会将这个任务交给池子里的一个空闲的goroutine去处理,如果池子里没有空闲的goroutine了,任务就会阻塞等待。所以协程池有三个角色Worker,Task,Pool。
  • 目录
    • 协程池模型
      • 属性定义
        • Task定义
        • Pool定义
        • Worker 定义
      • 方法定义
        • 协程池实现:

        Go语言虽然有着高效的GMP调度模型,理论上支持成千上万的goroutine,但是goroutine过多,对调度,gc以及系统内存都会造成压力,这样会使我们的服务性能不升反降。常用做法可以用池化技术,构造一个协程池,把进程中的协程控制在一定的数量,防止系统中goroutine过多,影响服务性能。

        协程池简单理解就是有一个池子一样的东西,里面装有固定数量的goroutine,当有一个任务到来的时候,会将这个任务交给池子里的一个空闲的goroutine去处理,如果池子里没有空闲的goroutine了,任务就会阻塞等待。所以协程池有三个角色WorkerTaskPool

        • Worker:用于执行任务的goroutine
        • Task: 具体的任务
        • Pool: 池子

        下面看一下各个角色的定义:

        Task有一个函数成员,表示这个task具体的执行逻辑: 

        type Task struct {
            f func() error  // 具体的执行逻辑
        }

          Pool有两个成员,Capacity表示池子里的worker的数量,即工作的goroutine的数量,JobCh表示任务队列用于存放任务,goroutine从这个JobCh获取任务执行任务逻辑: 

        type Pool struct {
            RunningWorkers int64     // 运行着的worker数量
            Capacity       int64     // 协程池worker容量---goroutine数量
            JobCh          chan *Task // 用于worker取任务
            sync.Mutex
        }

        // p为Pool对象指针
        for task := range p.JobCh {
            do ...      
        }

        执行任务单元,简单理解就是干活的goroutine,这个worker其实只做一件事情,就是不断的从任务队列里面取任务执行,而worker的数量就是协程池里协程的数量,由Pool的参数指定。 

        NewTask用于创建一个任务,参数是一个函数,返回值是一个Task类型。

        func NewTask(funcArg func() error) *Task

        NewPool返回一个协程数量固定为Capacity协程池对象指针,其任务队列的长度为taskNum。 

        func NewPool(Capacity int, taskNum int) *Pool

        接下来主要介绍协程池的各个方法:

        AddTask方法是往协程池添加任务,如果当前运行着的worker数量小于协程池worker容量,则立即启动一个协程worker来处理任务,否则将任务添加到任务队列。 

        func (p *Pool) AddTask(task *Task)

        Run方法将协程池跑起来,启动一个worker来处理任务。 

        func (p *Pool) Run()

        协程池处理任务流程图:

        package main
        
        import (
            "fmt"
            "sync"
            "sync/atomic"
            "time"
        )
        
        type Task struct {
            f func() error // 具体的任务逻辑
        }
        
        func NewTask(funcArg func() error) *Task {
            return &Task{
               f: funcArg,
            }
        }
        
        type Pool struct {
            RunningWorkers int64      // 运行着的worker数量
            Capacity       int64      // 协程池worker容量
            JobCh          chan *Task // 用于worker取任务
            sync.Mutex
        }
        
        func NewPool(capacity int64, taskNum int) *Pool {
            return &Pool{
               Capacity: capacity,
               JobCh:    make(chan *Task, taskNum),
            }
        }
        
        func (p *Pool) GetCap() int64 {
            return p.Capacity
        }
        
        func (p *Pool) incRunning() { // runningWorkers + 1
            atomic.AddInt64(&p.RunningWorkers, 1)
        }
        
        func (p *Pool) decRunning() { // runningWorkers - 1
            atomic.AddInt64(&p.RunningWorkers, -1)
        }
        
        func (p *Pool) GetRunningWorkers() int64 {
            return atomic.LoadInt64(&p.RunningWorkers)
        }
        
        func (p *Pool) run() {
            p.incRunning()
            go func() {
               defer func() {
                  p.decRunning()
               }()
               for task := range p.JobCh {
                  task.f()
               }
            }()
        }
        
        // AddTask 往协程池添加任务
        func (p *Pool) AddTask(task *Task) {
            // 加锁防止启动多个 worker
            p.Lock()
            defer p.Unlock()
        
            if p.GetRunningWorkers() < p.GetCap() { // 如果任务池满, 则不再创建 worker
               // 创建启动一个 worker
               p.run()
            }
        
            // 将任务推入队列, 等待消费
            p.JobCh <- task
        }
        
        func main() {
            // 创建任务池
            pool := NewPool(3, 10)
        
            for i := 0; i < 20; i++ {
               // 任务放入池中
               pool.AddTask(NewTask(func() error {
                  fmt.Printf("I am Taskn")
                  return nil
               }))
            }
        
            time.Sleep(1e9) // 等待执行
        }

         运行结果:

        I am Task
        I am Task
        I am Task
        I am Task
        I am Task
        I am Task
        I am Task
        I am Task
        I am Task
        I am Task
        I am Task
        I am Task
        I am Task
        I am Task
        I am Task
        I am Task
        I am Task
        I am Task
        I am Task
        I am Task

        程序创建了一个Worker数量为3,任务队列长度为10的协程池,往里面添加了20个任务,可以看到输出,一直只有3个worker在做任务,起到了控制goroutine数量的作用。

        到此这篇关于Go语言协程池的实现示例的文章就介绍到这了,更多相关Go语言协程池内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客!

        您可能感兴趣的文章:

        • golang协程池设计详解
        • GO实现协程池管理的方法
        • Golang线程池与协程池的使用
        • golang实现协程池的方法示例
        • Go简单实现协程池的实现示例
        • Golang协程池的实现与应用
        • 浅析在Go语言中如何实现协程池
        • golang中ants协程池使用和实现逻辑

        站内搜索