Prompts for GPT
User
gopool.go
package gopooltype Task func()
type GoPool struct { TaskQueue chan Task MaxWorkers int Workers []*Worker }
func NewGoPool(maxWorkers int) *GoPool { pool := &GoPool{ TaskQueue: make(chan Task), MaxWorkers: maxWorkers, Workers: make([]*Worker, maxWorkers), } for i := 0; i < maxWorkers; i++ { worker := newWorker(pool.TaskQueue) pool.Workers[i] = worker worker.start() } return pool }
func (p *GoPool) AddTask(task Task) { p.TaskQueue <- task }
func (p *GoPool) Release() { close(p.TaskQueue) for _, worker := range p.Workers { <-worker.TaskQueue } }
worker.go
package gopooltype Worker struct { TaskQueue chan Task }
func newWorker(taskQueue chan Task) *Worker { return &Worker{ TaskQueue: taskQueue, } }
func (w *Worker) start() { go func() { for task := range w.TaskQueue { if task != nil { task() } } }() }
The performance test results are not satisfactory. I suspect it might be due to all workers consuming the same TaskQueue, which requires too many locks. Perhaps we can make the following modifications:
- Remove GoPool.TaskQueue. When AddTask is invoked, directly throw the Task into Worker.TaskQueue.
- Retain a Task Channel with a capacity of 1 in Worker.
- AddTask needs to retrieve an idle Worker from GoPool.Workers. After the Worker finishes processing the Task, it needs to be returned to GoPool. Therefore, you should consider implementing workers as a stack, adding corresponding push and pop methods.
DevChat
In accordance with your feedback, I have made some modifications to the code. Here is the modified code:
In the gopool.go
file:
package gopool
type Task func()
type GoPool struct {
Workers []*Worker
MaxWorkers int
workerStack []int
}
func NewGoPool(maxWorkers int) *GoPool {
pool := &GoPool{
MaxWorkers: maxWorkers,
Workers: make([]*Worker, maxWorkers),
workerStack: make([]int, maxWorkers),
}
for i := 0; i < maxWorkers; i++ {
worker := newWorker()
pool.Workers[i] = worker
pool.workerStack[i] = i
worker.start(pool, i)
}
return pool
}
func (p *GoPool) AddTask(task Task) {
workerIndex := p.popWorker()
p.Workers[workerIndex].TaskQueue <- task
}
func (p *GoPool) Release() {
for _, worker := range p.Workers {
close(worker.TaskQueue)
}
}
func (p *GoPool) popWorker() int {
workerIndex := p.workerStack[len(p.workerStack)-1]
p.workerStack = p.workerStack[:len(p.workerStack)-1]
return workerIndex
}
func (p *GoPool) pushWorker(workerIndex int) {
p.workerStack = append(p.workerStack, workerIndex)
}
In the worker.go
file:
package gopool
type Worker struct {
TaskQueue chan Task
}
func newWorker() *Worker {
return &Worker{
TaskQueue: make(chan Task, 1),
}
}
func (w *Worker) start(pool *GoPool, workerIndex int) {
go func() {
for task := range w.TaskQueue {
if task != nil {
task()
}
pool.pushWorker(workerIndex)
}
}()
}
In this implementation, the TaskQueue
in the GoPool
struct has been removed, and each Worker
has its own TaskQueue
. The AddTask
method will add tasks to the TaskQueue
of an idle Worker
. The Release
method will close the TaskQueue
of all Workers
. The popWorker
and pushWorker
methods have been added for retrieving and returning Worker
from the workerStack
.