当前位置: 技术文章>> 100道Go语言面试题之-在Go中,如何实现一个简单的协程池(Goroutine Pool)?

文章标题:100道Go语言面试题之-在Go中,如何实现一个简单的协程池(Goroutine Pool)?
  • 文章分类: 后端
  • 6354 阅读

在Go语言中,协程(Goroutine)是轻量级的线程,由Go运行时管理,用于并发执行。然而,在某些情况下,无限制地创建Goroutine可能会导致资源(如CPU和内存)的过度使用,特别是在处理大量并发任务时。为了解决这个问题,我们可以实现一个协程池(Goroutine Pool),它限制了同时运行的Goroutine的数量,并在这些Goroutine完成后复用它们,而不是每次都创建新的。

下面是一个简单的协程池实现的例子,使用channelsync.WaitGroup来控制并发和等待:

package main

import (
    "fmt"
    "sync"
    "time"
)

// GoroutinePool 协程池结构
type GoroutinePool struct {
    maxGoroutines int
    queue         chan func()
    wg            sync.WaitGroup
}

// NewGoroutinePool 创建一个新的协程池
func NewGoroutinePool(maxGoroutines int) *GoroutinePool {
    return &GoroutinePool{
        maxGoroutines: maxGoroutines,
        queue:         make(chan func(), maxGoroutines),
    }
}

// Start 启动协程池
func (p *GoroutinePool) Start() {
    for i := 0; i < p.maxGoroutines; i++ {
        p.wg.Add(1)
        go func() {
            defer p.wg.Done()
            for job := range p.queue {
                job()
            }
        }()
    }
}

// Submit 提交一个任务到协程池
func (p *GoroutinePool) Submit(job func()) {
    p.queue <- job
}

// Wait 等待所有任务完成
func (p *GoroutinePool) Wait() {
    p.wg.Wait()
    close(p.queue) // 所有任务完成后关闭channel
}

func main() {
    pool := NewGoroutinePool(5)
    pool.Start()

    for i := 0; i < 20; i++ {
        idx := i
        pool.Submit(func() {
            fmt.Printf("Running job %d\n", idx)
            time.Sleep(1 * time.Second) // 模拟耗时任务
        })
    }

    pool.Wait()
    fmt.Println("All jobs completed")
}

解释

  1. 结构体定义GoroutinePool 结构体包括一个最大Goroutine数量、一个任务队列(channel)和一个sync.WaitGroup用于等待所有Goroutine完成。

  2. NewGoroutinePool:创建一个新的协程池实例,并初始化maxGoroutinesqueue

  3. Start:启动指定数量的Goroutine,每个Goroutine都从任务队列中取出并执行任务,直到队列关闭。

  4. Submit:将一个任务(一个无参数无返回值的函数)放入任务队列中。

  5. Wait:等待所有已提交的任务完成。使用sync.WaitGroup确保所有Goroutine都已完成。

  6. 主函数:创建协程池,提交多个任务,并等待所有任务完成。

这个简单的协程池示例展示了如何限制并发执行的任务数量,并通过复用Goroutine来避免过度创建新线程的开销。然而,请注意,对于复杂的生产环境,可能需要考虑更多的因素,如任务超时、优雅关闭等。

推荐文章