go并发编程系列七:手写一个go线程池

背景:提到线程池,我们会有大概的印象,通常我们对线程池的理解是:一组活跃的线程,但是这种理解是片面的,不完整的,为此,在手写线程池之前,我们一定要明确线程池到底是什么?不要以我以为的方式去写代码。

 

一、线程池有以下要素组成

线程队列(Task Queue):用于存储待执行的任务,通常是一个先进先出(FIFO)队列。

工作线程池(Worker Threads):由一组已创建的线程组成,它们等待从任务队列中获取任务并执行它们。

管理器(Manager):负责将任务分配给空闲的工作线程,并监控线程池的状态。

我们说线程池是一组活跃的线程组,只答对了一部分,线程池还包括:线程队列。 

 

先不考虑管理器,我们定义线程池模型:Pool,代码如下:

// Pool 表示线程池。
type Pool struct {
    Workers  []Worker
    TaskQueue chan func()
    wg       sync.WaitGroup
}

 

定义工作线程池模型:Worker,代码如下:

// Worker 表示工作池中的工作人员。
type Worker struct {
    ID int
}

 二、为线程池模型Pool定义方法

根据线程池的组成要素,我们知道线程池Pool有一个队列TaskQueue,相应的Pool需要一个入队的方法,我们定义为Submit,代码如下:

// Submit 提交一个任务给线程池。
func (p *Pool) Submit(task func()) {
    p.wg.Add(1)
    p.TaskQueue <- task
}

 

同时,还应该考虑关闭线程池Pool的情况,关闭线程池实际上就是关闭线程池的任务队列,而我们的任务队列是使用chan信道实现的,因此,我们可以简单的理解为:关闭线程池就是关闭线程池的任务队列,本质上是关闭线程池任务队列对应的信道。

我们定义一个名为Shutdown的方法,代码如下:

// Shutdown 关闭线程池,等待所有任务完成。
func (p *Pool) Shutdown() {
    close(p.TaskQueue)
    p.wg.Wait()
}

 

要想使用线程池,还需要一个创建线程池的函数,我们定义为NewPool,代码如下:

// NewPool 创建一个新的线程池。
func NewPool(numWorkers int, taskQueueSize int) *Pool {
    pool := &Pool{
        TaskQueue: make(chan func(), taskQueueSize),
    }

    pool.Workers = make([]Worker, numWorkers)
    for i := 0; i < numWorkers; i++ {
        pool.Workers[i] = Worker{ID: i}
    }

    return pool
}

 

经过上述操作,线程池的雏形已经完成,为了让线程池能够处理队列里的任务,我们还需要为工作线程池Worker定义一个Start方法,代码如下:

// Start 启动工作人员,等待任务并执行。
func (w Worker) Start(p *Pool) {
    go func() {
        for task := range p.TaskQueue {
            task() // 执行任务函数
            p.wg.Done()
        }
    }()
}

 至此,一个能处理队列任务的线程池模型Pool就完成了,但是,该怎么用呢?请看下面的讲解。

 

三、使用线程池Pool处理队列任务

使用线程池的步骤分为:

1:通过NewPool创建线程池

2:启动线程池的工作线程池

3:将待处理的任务交给线程池队列,由线程池里的工作线程池进行处理

func main() {
    pool := NewPool(3, 10)

    // 启动工作人员
    for _, worker := range pool.Workers {
        worker.Start(pool)
    }

    // 提交一些任务到线程池
    for i := 0; i < 5; i++ {
        id := i
        pool.Submit(func() {
            fmt.Printf("Task %d executed by Worker %d\n", id, id%3)
        })
    }

    // 关闭线程池并等待所有任务完成
    pool.Shutdown()
}

 

posted @ 2023-09-07 18:13  jamstack  阅读(223)  评论(0编辑  收藏  举报