Golang协程池gopool设计与实现

yizhihongxing

Golang协程池gopool设计与实现

协程池的概念

在 Golang 中,我们可以通过 Go 关键字,轻松创建协程(也称作 goroutine),但这种方式也会导致大量的协程被创建,如果这些协程的生命周期很短,那么会导致频繁的创建和销毁,带来较大的系统开销。此时,协程池就应运而生了。协程池的工作原理是,创建一些协程并将它们放到一个池子里面,并在需要使用协程时,从池子中取出一个协程来执行任务,任务完成后再将协程放回池子中,从而避免了频繁地创建和销毁协程。

gopool的实现原理

gopool 是一个 Golang 协程池的工具库,通过封装 sync.Pool 和 Golang 协程实现。gopool 提供了一个分配协程的入口,我们可以在程序里调用该函数来申请一个协程来执行任务。

gopool 包含以下几个部分的实现:

协程池结构体

type Pool interface {
    Stop()
    Serve(context.Context) error
}

type pool struct {
    mu          sync.Mutex
    cond        *sync.Cond
    cap         int             // 协程池容量
    running     int             // 正在执行的协程数
    workers     []*worker       // 协程池所包含的工人
    expiredTime time.Duration   // 协程空闲超时时间
    ctx         context.Context // 上下文
    cancel      context.CancelFunc
}

工人结构体

type worker struct {
    pool *pool
    task chan func() // 实际执行任务的通道
    used int64       // 工人使用次数
    expire int64     // 工人的过期时间
}

协程池运行方法

func (p *pool) Serve(ctx context.Context) error {
    for {
        select {
        case <-ctx.Done():
            return nil
        default:
        }

        if len(p.workers) >= p.cap {
            return nil // 达到最大并发数量
        }

        w := &worker{
            pool: p,
            task: make(chan func(), 1),
        }
        p.mu.Lock()
        p.workers = append(p.workers, w)
        p.mu.Unlock()

        go w.run()

        select {
        case <-ctx.Done():
            return nil
        default:
        }
    }
}

工人运行方法

func (w *worker) run() {
    for {
        w.pool.cond.L.Lock() // 获取锁

        for len(w.task) == 0 {
            when := time.Now().UnixNano()

            // 判断是否已经过期了
            d := w.expire - when
            if d <= 0 {
                w.pool.removeWorker(w)
                w.pool.cond.L.Unlock()
                return // 过期了,退出
            }

            w.pool.cond.L.Unlock()

            time.Sleep(time.Duration(d))

            w.pool.cond.L.Lock()
        }

        // 从任务队列中收取任务
        task := <-w.task
        w.pool.running++
        w.pool.cond.L.Unlock()

        // 执行任务
        task()

        // 通知任务已完成,并放回任务池中
        w.pool.cond.L.Lock()
        w.pool.running--
        w.pool.putTask(task)
        w.pool.cond.L.Unlock()
    }
}

协程池停止方法

为了保证在没有任务时,协程池可以正确的退出,需要实现一个协程锁,用来控制工作协程停止工作。

// 停止协程池
func (p *pool) Stop() {
    p.cancel()
    p.mu.Lock()

    for _, worker := range p.workers {
        worker.stop()
    }

    p.workers = nil
    p.running = 0

    p.mu.Unlock()
}

gopool使用示例

下面,我们就来看两个使用 gopool 的例子:计算斐波那契数列和从文件中读入数据。

计算斐波那契数列

func Fibonacci(n int) int {
    if n == 0 {
        return 0
    }

    if n == 1 {
        return 1
    }

    return Fibonacci(n-1) + Fibonacci(n-2)
}

// 计算斐波那契数列和
func CalculateFibonacci(n int) int {
    if n <= 0 {
        return 0
    }

    if n == 1 {
        return 1
    }

    task := make(chan func())

    p := newGoroutinePool(10, 10)
    defer p.Stop()

    for i := 0; i < n; i++ {
        i := i
        p.Execute(func() {
            task <- func() {
                Fibonacci(i)
            }
        }, nil)
    }

    result := make(chan int, n)

    for i := 0; i < n; i++ {
        p.Execute(nil, func() {
            result <- <-task
        })
    }

    total := 0
    for i := 0; i < n; i++ {
        total += <-result
    }

    return total
}

从文件中读入数据

func countWords(fn string, filter func(string) bool) (int, error) {
    var count int

    file, err := os.Open(fn)
    if err != nil {
        return 0, err
    }

    defer file.Close()

    scanner := bufio.NewScanner(file)

    task := make(chan func())

    p := newGoroutinePool(10, 10)
    defer p.Stop()

    for scanner.Scan() {
        line := scanner.Text()

        p.Execute(func() {
            task <- func() {
                if filter == nil || filter(line) {
                    count += len(strings.Split(line, " "))
                }
            }
        }, nil)
    }

    if err := scanner.Err(); err != nil {
        return 0, err
    }

    for {
        if len(task) == 0 {
            break
        }

        p.Execute(nil, func() {
            <-task
        })
    }

    return count, nil
}

以上是 gopool 的使用示例,可以看出,gopool 的使用相对简单,通过传入一个需要执行的函数即可。同时,也可以从以上示例中发现 gopool 的协程池确实可以很好的提高程序的性能。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Golang协程池gopool设计与实现 - Python技术站

(0)
上一篇 2023年6月27日
下一篇 2023年6月27日

相关文章

  • 批处理命令教学之复合语句连接符(&、&&和||)

    批处理命令教学之复合语句连接符(&、&&和||) 在命令行执行批处理操作时,我们经常需要同时执行多个命令,或者根据之前的命令结果来决定是否执行后续的命令,这时就需要用到复合语句连接符。下面分别介绍&、&&和||三种复合语句连接符的使用方法。 &(连接符) &连接符可以同时执行两个及以上的命令,用…

    other 2023年6月26日
    00
  • css新单位fr

    CSS新单位fr的完整攻略 CSS新单位fr是CSS Grid Layout中的一种新单位,它表示可用空间的一部分。本文将提供一个完整攻略,包括fr定义、使用方法、示例说明等。 1. 定义 fr是CSS Grid中的一种新单位,它表示可空间的一分。fr是“fraction”的缩写,意为“分”。一个fr表示可用空间的一部分,例如,如果一个容器有3个,另一个容器…

    other 2023年5月8日
    00
  • Python命名空间的本质和加载顺序

    Python命名空间的本质和加载顺序攻略 Python中的命名空间是一个用于存储变量名称和其对应对象的映射关系的系统。在Python中,每个对象都存储在一个命名空间中,以便在代码中进行访问和使用。本攻略将详细讲解Python命名空间的本质和加载顺序,并提供两个示例来说明。 1. 命名空间的本质 命名空间是一个字典对象,用于存储变量名称和其对应对象的映射关系。…

    other 2023年8月8日
    00
  • iOS开发中使用Quartz2D绘图及自定义UIImageView控件

    让我们来详细讲解一下“iOS开发中使用Quartz2D绘图及自定义UIImageView控件”的完整攻略。 1. 简介 在iOS开发中,我们常常需要使用到Quartz2D进行绘图。Quartz2D是一个二维绘图引擎,可以实现各种各样的绘图效果。同时,自定义UIImageView控件也能够大大提升APP的展示效果和用户体验度。 2. 使用Quartz2D绘图 …

    other 2023年6月25日
    00
  • springboot集成测试容器重启问题的处理

    Spring Boot集成测试容器重启问题的处理 在Spring Boot集成测试中,使用测试容器(Testcontainers)可以方便地集成外部依赖,并在运行测试时动态启动和关闭它们。然而,有时候测试容器的重启会导致测试失败,本文将介绍如何解决这个问题。 问题描述 当Spring Boot应用程序启动测试容器并运行一些测试之后,测试容器将会被重新启动并重…

    other 2023年6月27日
    00
  • C语言中带头双向循环链表基本操作的实现详解

    C语言中带头双向循环链表基本操作的实现详解 什么是带头双向循环链表 带头双向循环链表是一种常见的数据结构,在实际开发中也经常会用到。带头双向循环链表可以看作是一种特殊的链表,相对于普通链表,它具有以下特点: 它有一个头结点,头结点不存储数据,它的作用是指向链表中的第一个节点。 每个节点都有一个前驱指针prev和一个后继指针next,用于指向前一个节点和后一个…

    other 2023年6月27日
    00
  • MySQL数据库压缩版本安装与配置详细教程

    MySQL数据库压缩版本安装与配置详细教程 安装步骤 下载MySQL压缩版本 前往MySQL官网下载MySQL压缩版本(Community Server),根据操作系统位数选择相应版本。 将下载的文件移动到目标安装路径,准备解压安装。 bash mv ~/Downloads/mysql-x.x.xx.tar.gz /usr/local/mysql 解压MyS…

    other 2023年6月20日
    00
  • pythonmysql模块

    pythonmysql模块 Python是一门高级编程语言,它被广泛地应用于各个领域。在Web开发中,Python是最常用的编程语言之一。Python通过pip工具提供了丰富的第三方模块,可以大幅度提升开发效率和开发质量。 本文主要介绍Python中的mysql模块,它是Python中操作MySQL数据库的主要工具。 安装mysql模块 在使用mysql模块…

    其他 2023年3月29日
    00
合作推广
合作推广
分享本页
返回顶部