• 设为首页
  • 收藏本站
  • 积分充值
  • VIP赞助
  • 手机版
  • 微博
  • 微信
    微信公众号 添加方式:
    1:搜索微信号(888888
    2:扫描左侧二维码
  • 快捷导航
    福建二哥 门户 查看主题

    go协程池实现原理小结

    发布者: 琛瑞6678 | 发布时间: 2025-8-14 13:41| 查看数: 43| 评论数: 0|帖子模式

    在go语言编程中有一种池肯定避免不了,那就是-协程池,无论你是日常工作还是面试中面试官都无法避免协程池,掌握协程池你也就算是入门go的并发编程了,打一波广告后面会有专门的文章来介绍如何在go中进行并发编程。
    协程本身也是一种资源,但是协程池有自己的特殊性,那是由协程池执行任务的特殊性决定的,协程作为资源使用时其实是在消费其他资源,也就是说必须向协程池提供一个统一的接口,所有需要协程池执行的任务都需要实现该接口,然后被协程池统一调用。
    这里我们就要求所有需要被协程执行的函数都要实现Worker接口的Task方法
    1. type Worker interface {
    2.         Task()
    3. }
    复制代码
    另外一个特殊的点,在于go协程执行时特别是用户添加任务时最好能够让用户能够感知到协程池当前的工作状态,因此这里采用无缓冲区的chan作为协程池任务传递工具,能直接根据向chan添加任务时的状态感知到当前协程池的工作状态。这里采用最简单的阻塞方式来实现,当协程池忙的情况下直接阻塞直到协程池空闲用户才能将自己的任务添加到协程池的管道中进行执行。

    go中使用协程进行工作,因此会创建并使用协程池进行工作非常的有必要,work 包的目的是展示如何使用无缓冲的通道来创建一个 goroutine 池,这些 goroutine 执行并控制一组工作,让其并发执行。在这种情况下,使用无缓冲的通道要比随意指定一个缓冲区大小的有缓冲的通道好,因为这个情况下既不需要一个工作队列,也不需要一组 goroutine 配合执work 包的目的是展示如何使用无缓冲的通道来创建一个 goroutine 池,这些 goroutine 执行并控制一组工作,让其并发执行。在这种情况下,使用无缓冲的通道要比随意指定一个缓冲区大小的有缓冲的通道好,因为这个情况下既不需要一个工作队列,也不需要一组goroutine 配合执

    结构体

    协程池工作比较单一,就是调用指定的Task方法,另外因为给出任务时最好能立刻执行或者不能立刻执行需要让用户等待,因此协程池最好使用无缓冲的通道,这样当用户需要执行Task时就能直接从接口中感知到当前协程池是否空闲了。
    1. type Worker interface {
    2.         Task()
    3. }type Pool struct {        // 使用无缓冲通道实现协程池        work chan Worker        // 辅助计数器,用于协程池同步        wg   sync.WaitGroup}
    复制代码
    创建协程池

    创建协程池需要指定最大并发数,当有新的任务加入时,会立即被执行,而当没有任务时,所有协程池中的协程阻塞竞争等待通道中的"任务"。
    1. // New 创建一个工作池
    2. func New(maxGoroutine int) *Pool {
    3.         p := Pool{
    4.                 work: make(chan Worker),
    5.         }
    6.         p.wg.Add(maxGoroutine)
    7.         for i := 0; i < maxGoroutine; i++ {
    8.                 go func() {
    9.                         // 一直循环取任务,直到work被关闭为止并且通道中的任务执行完毕为止
    10.                         for w := range p.work {
    11.                                 w.Task()
    12.                         }
    13.                         // 退出时候,减少一个计数器
    14.                         p.wg.Done()
    15.                 }()
    16.         }
    17.         return &p
    18. }
    复制代码
    协程池启动和关闭

    触发协程池工作很简单,只需要向协程池等待的通道中放入一个任务即可,当协程池关闭时,所有任务都会被立即执行,当所有任务执行完毕,协程池中的所有协程都会退出。
    1. // Run 将任务放入工作池
    2. func (p *Pool) Run(w Worker) {
    3.         p.work <- w
    4. }

    5. // Shutdown 等待所有goroutine完成工作
    6. func (p *Pool) Shutdown() {
    7.         // 关闭work所有协程完成任务之后会退出for循环
    8.         close(p.work)
    9.         p.wg.Wait()
    10. }
    复制代码
    将上述实现汇总之后如下

    work/work.go
    1. package workimport "sync"// Worker interface 必须满足worker的要求才能使用工作池type Worker interface {
    2.         Task()
    3. }// Pool 提供一个goroutine池,这个池可以完成任何已提交的woker任务type Pool struct {        work chan Worker        wg   sync.WaitGroup}// New 创建一个工作池func New(maxGoroutine int) *Pool {        p := Pool{                work: make(chan Worker),        }        p.wg.Add(maxGoroutine)        for i := 0; i < maxGoroutine; i++ {                go func() {                        // 一直循环取任务,直到work被关闭为止                        for w := range p.work {                                w.Task()                        }                        // 退出时候,减少一个计数器                        p.wg.Done()                }()        }        return &p}// Run 将任务放入工作池
    4. func (p *Pool) Run(w Worker) {
    5.         p.work <- w
    6. }

    7. // Shutdown 等待所有goroutine完成工作
    8. func (p *Pool) Shutdown() {
    9.         // 关闭work所有协程完成任务之后会退出for循环
    10.         close(p.work)
    11.         p.wg.Wait()
    12. }
    复制代码
    对实现的接口进行功能测试
    1. // This sample program demonstrates how to use the work package
    2. // to use a pool of goroutines to get work done.
    3. package main

    4. import (
    5.         "log"
    6.         "sync"
    7.         "time"

    8.         "work"
    9. )

    10. // names provides a set of names to display.
    11. var names = []string{
    12.         "steve",
    13.         "bob",
    14.         "mary",
    15.         "therese",
    16.         "jason",
    17. }

    18. // namePrinter provides special support for printing names.
    19. type namePrinter struct {
    20.         name string
    21. }

    22. // Task implements the Worker interface.
    23. func (m *namePrinter) Task() {
    24.         log.Println(m.name)
    25.         time.Sleep(time.Second)
    26. }

    27. // main is the entry point for all Go programs.
    28. func main() {
    29.         // Create a work pool with 2 goroutines.
    30.         p := work.New(2)

    31.         var wg sync.WaitGroup
    32.         wg.Add(100 * len(names))

    33.         for i := 0; i < 100; i++ {
    34.                 // Iterate over the slice of names.
    35.                 for _, name := range names {
    36.                         // Create a namePrinter and provide the
    37.                         // specific name.
    38.                         np := namePrinter{
    39.                                 name: name,
    40.                         }

    41.                         go func() {
    42.                                 // Submit the task to be worked on. When RunTask
    43.                                 // returns we know it is being handled.
    44.                                 p.Run(&np)
    45.                                 wg.Done()
    46.                         }()
    47.                 }
    48.         }

    49.         wg.Wait()

    50.         // Shutdown the work pool and wait for all existing work
    51.         // to be completed.
    52.         p.Shutdown()
    53. }
    复制代码
    到此这篇关于go协程池实现原理小结的文章就介绍到这了,更多相关go 协程池内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

    来源:互联网
    免责声明:如果侵犯了您的权益,请联系站长(1277306191@qq.com),我们会及时删除侵权内容,谢谢合作!

    本帖子中包含更多资源

    您需要 登录 才可以下载或查看,没有账号?立即注册

    ×

    最新评论

    QQ Archiver 手机版 小黑屋 福建二哥 ( 闽ICP备2022004717号|闽公网安备35052402000345号 )

    Powered by Discuz! X3.5 © 2001-2023

    快速回复 返回顶部 返回列表