Golang协程池gopool设计与实现

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 的协程池确实可以很好的提高程序的性能。

阅读剩余 81%

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

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

相关文章

  • vivo X Fold2开发者模式在哪 vivo X Fold2进入开发者模式的方法

    以下是“vivo X Fold2开发者模式在哪 vivo X Fold2进入开发者模式的方法”的完整攻略: 一、vivo X Fold2开发者模式在哪 要在vivo X Fold2中找到开发者模式,可以按照以下步骤进行操作: 打开设置应用。可以通过点击主屏幕上的“设置”图标或从通知栏中下拉通知栏,然后点击“设置”来打开设置应用。 向下滑动屏幕,找到“关于手机…

    other 2023年6月26日
    00
  • Lombok中@Builder和@SuperBuilder注解的用法案例

    Lombok 是 Java 开发中最常用的工具类库之一,它的作用是帮助我们简化代码、提高开发效率。其中,@Builder 和 @SuperBuilder 两个注解是 Lombok 中非常实用的注释。下面,我们将详细讲解它们的用法,并提供两个案例说明。 @Builder 注解 @Builder 注解是 Lombok 提供的一种非常方便的功能,可以帮助我们生成一…

    other 2023年6月26日
    00
  • thinkPHP框架中layer.js的封装与使用方法示例

    下面是 “thinkPHP框架中layer.js的封装与使用方法示例” 的攻略: 1. layer.js的引入和初始化 1.1 引入layer.js 在HTML页面中通过script标签引入layer.js文件,代码示例如下: <script src="/path/to/layer.js"></script> 1.…

    other 2023年6月25日
    00
  • 小米盒子怎么重启? 小米盒子快速重启和清理缓存的教程

    下面是小米盒子重启、快速重启和清理缓存的教程: 小米盒子如何重启 若要重启小米盒子,有两种方法: 使用遥控器进行重启。按住遥控器上的电源键,等待出现“电源菜单”界面,然后选择“重启”即可。 使用小米盒子设置界面进行重启。进入小米盒子的“设置”界面,选择“存储与重置”,然后选择“重启设备”即可。 小米盒子如何快速重启 快速重启小米盒子可以清除设备内的缓存,以便…

    other 2023年6月26日
    00
  • 安卓/iOS版Skype 6.0正式发布及提供下载地址

    安卓/iOS版Skype 6.0正式发布及提供下载地址攻略 Skype是一款广受欢迎的即时通讯和语音通话应用程序。最新版本的Skype 6.0已经正式发布,并提供了安卓和iOS版的下载。本攻略将详细介绍如何下载和安装Skype 6.0,并提供下载地址。 下载和安装Skype 6.0 打开安卓或iOS设备上的应用商店(Google Play Store或App…

    other 2023年8月4日
    00
  • numpy与list之间的转换

    numpy与list之间的转换 在进行数据处理和科学计算时,numpy是一款非常强大的工具。Numpy提供了许多用于处理多维数组及矩阵的函数,可以有效提高处理数据的效率及精度。但有时候,我们需要将numpy数组转换为Python的列表(List)类型,或反过来进行转换。本文将介绍numpy数组和Python列表之间的转换方式。 1.将list转为ndarra…

    其他 2023年3月28日
    00
  • swipe.js文档

    什么是swipe.js? swipe.js是一个轻量级的JavaScript,用于创建响应式的、可触摸滑动幻灯片。它支持多种滑动效果和自定义选项,可以轻松集成到您的网站或应用程序中。 如何使用swipe.js? 以下是使用swipe.js的步骤: 引入swipe文件。 “`html “` 创建HTML结构。 “`html Slide 1 Slide 2…

    other 2023年5月7日
    00
  • 分享15个最佳的HTML/CSS设计和开发框架

    分享15个最佳的HTML/CSS设计和开发框架攻略 在这个攻略中,我将向您介绍15个最佳的HTML/CSS设计和开发框架。这些框架可以帮助您快速构建美观、响应式的网页和应用程序。以下是每个框架的简要介绍和两个示例说明。 1. Bootstrap Bootstrap是一个流行的前端框架,提供了丰富的CSS和JavaScript组件,用于构建现代化的网页和应用程…

    other 2023年7月27日
    00
合作推广
合作推广
分享本页
返回顶部