Prompts for GPT

avatar User

gopool.go package gopool

type 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 gopool

type 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:

  1. Remove GoPool.TaskQueue. When AddTask is invoked, directly throw the Task into Worker.TaskQueue.
  2. Retain a Task Channel with a capacity of 1 in Worker.
  3. 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.

avatar 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.