go工作池模板记录

go工作池模板记录

Go语言中的工作池(Worker Pool)是一种常用的并发模式,它可以有效地管理和复用一组固定数量的goroutine(Go协程),以处理并发任务。

工作池的基本思想是预先创建一组goroutine,这些goroutine可以并行地执行任务。当有新的任务到达时,它们会被提交到工作池,由其中的一个空闲goroutine来处理任务。一旦任务完成,该goroutine就会回到空闲状态,可以继续处理下一个任务

package workPool

import (
	"fmt"
	"sync"
	"testing"
)

type Job struct {
	// 定义任务结构
	// 可根据实际需求进行修改和扩展
	ID   int
	Data interface{}
}
type Result struct {
	// 定义结果结构
	// 可根据实际需求进行修改和扩展
	JobID int
	Data  interface{}
}
type Worker struct {
	ID         int
	JobChannel <-chan Job
	ResultChan chan<- Result
	QuitChan   chan bool
}

func NewWorker(id int, jobChannel <-chan Job, resultChan chan<- Result) *Worker {
	return &Worker{
		ID:         id,
		JobChannel: jobChannel,
		ResultChan: resultChan,
		QuitChan:   make(chan bool),
	}
}

func (w *Worker) Start() {
	go func() {
		for {
			select {
			case job := <-w.JobChannel:
				result := w.Process(job)
				w.ResultChan <- result
			case <-w.QuitChan:
				return
			}
		}
	}()
}

func (w *Worker) Process(job Job) Result {
	// 执行任务的逻辑
	// 可根据实际需求进行修改和扩展
	result := Result{
		JobID: job.ID,
		Data:  nil, // 根据任务逻辑设置结果数据
	}
	return result
}

type Pool struct {
	Workers     []*Worker
	JobChannel  chan Job
	ResultChan  chan Result
	QuitChannel chan bool
	wg          sync.WaitGroup
}

func NewPool(numWorkers, jobQueueSize, resultQueueSize int) *Pool {
	workers := make([]*Worker, numWorkers)
	jobChannel := make(chan Job, jobQueueSize)
	resultChannel := make(chan Result, resultQueueSize)
	quitChannel := make(chan bool)
	return &Pool{
		Workers:     workers,
		JobChannel:  jobChannel,
		ResultChan:  resultChannel,
		QuitChannel: quitChannel,
	}
}
func (p *Pool) Start() {
	for _, worker := range p.Workers {
		worker.Start()
	}
	go p.processResults()
}
func (p *Pool) processResults() {
	for result := range p.ResultChan {
		// 处理结果的逻辑
		// 可根据实际需求进行修改和扩展
		fmt.Printf("Job ID: %d, Result: %v\n", result.JobID, result.Data)
		p.wg.Done()
	}

	p.QuitChannel <- true
}

func (p *Pool) AddJob(job Job) {
	p.wg.Add(1)
	p.JobChannel <- job
}

func (p *Pool) Wait() {
	p.wg.Wait()
	close(p.ResultChan)
	<-p.QuitChannel
}
func TestDemo(t *testing.T) {
	numWorkers := 3
	jobQueueSize := 10
	resultQueueSize := 10

	pool := NewPool(numWorkers, jobQueueSize, resultQueueSize)

	for i := 0; i < numWorkers; i++ {
		worker := NewWorker(i+1, pool.JobChannel, pool.ResultChan)
		pool.Workers[i] = worker
	}
	pool.Start()

	// 添加任务到工作池
	for i := 0; i < jobQueueSize; i++ {
		job := Job{
			ID:   i + 1,
			Data: nil, // 根据任务逻辑设置任务数据
		}
		pool.AddJob(job)
	}

	pool.Wait()

	fmt.Println("All jobs completed.")

}

在这个模板化的工作池示例中,您可以根据实际项目的需求进行修改和扩展。

  • Job 结构定义了任务的字段,您可以根据实际需求进行修改和扩展。
  • Result 结构定义了结果的字段,您可以根据实际需求进行修改和扩展。
  • Worker 结构表示工作池中的一个worker,其中的 Process 方法定义了执行任务的逻辑,您可以根据实际需求进行修改。
  • Pool 结构包含了一个worker集合、任务通道、结果通道和退出通道,其中的 processResults 方法用于处理任务结果,您可以根据实际需求进行修改。
  • NewPool 函数用于创建一个新的工作池,其中的参数可以根据实际项目需求进行调整。
  • AddJob 方法用于将任务添加到工作池中。
  • Wait 方法用于等待所有任务完成,并确保工作池中的所有goroutine都已退出。

通过修改和扩展上述代码,您可以根据实际项目的需求来创建一个适用的工作池。您可以调整worker的数量、定义适当的任务和结果结构,以及自定义任务处理逻辑和结果处理逻辑。

问题一:为什么正在执行任务的协程不会去竞争新的任务

回答:

在上述代码中,当有10个任务被提交到工作池,而工作协程只有3个时,情况如下:

前3个任务会立即被分配给3个工作协程,并开始执行任务。
一旦工作协程完成了一个任务,它会从JobChannel中获取下一个可用任务并继续执行。
后续任务会被放入JobChannel中等待被工作协程取出并执行。
在这个过程中,如果没有空闲的工作协程可用,即所有工作协程都在忙于执行任务,那么提交的任务会在JobChannel中等待,直到有工作协程空闲并从通道中接收任务为止。这就是为什么后续任务会被阻塞的原因。

默认情况下,case job := <-w.JobChannel是阻塞的。如果将default分支添加到select语句中,它可以执行一些其他操作,例如丢弃任务或打印日志,但并不会改变工作协程是否阻塞在case job := <-w.JobChannel这一行上。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值