Как правильно организовать структуру Worker Pool?

Организация структуры Worker Pool в Go является очень полезным и эффективным подходом для обработки асинхронных задач. Worker Pool представляет собой группу рабочих горутин (goroutines), которые выполняют задачи из очереди в фоновом режиме. Это позволяет контролировать количество одновременно выполняющихся задач и избежать перегрузки системы ресурсами.

Для начала необходимо создать структуру, представляющую рабочего. Рабочий должен иметь доступ к входной очереди задач, а также к каналу для коммуникации с главной горутиной. Он также должен уметь выполнять задачи из очереди до тех пор, пока не получит сигнал остановки:

type Worker struct {
    ID     int
    Tasks  chan Task
    Quit   chan bool
}

func NewWorker(id int) Worker {
    return Worker{
        ID:     id,
        Tasks:  make(chan Task),
        Quit:   make(chan bool),
    }
}

func (w Worker) Start() {
    go func() {
        for {
            select {
            case task := <-w.Tasks:
                // выполнить задачу
            case <-w.Quit:
                return
            }
        }
    }()
}

func (w Worker) Stop() {
    go func() {
        w.Quit <- true
    }()
}

Теперь создадим структуру Worker Pool, которая будет содержать группу рабочих, а также входную очередь задач и канал для ожидания завершения работы всех рабочих:

type WorkerPool struct {
    Workers       []Worker
    Tasks         chan Task
    Completion    chan bool
}

func NewWorkerPool(size int) WorkerPool {
    pool := WorkerPool{
        Workers:       make([]Worker, size),
        Tasks:         make(chan Task),
        Completion:    make(chan bool),
    }

    for i := 0; i < size; i++ {
        pool.Workers[i] = NewWorker(i)
    }

    return pool
}

func (pool WorkerPool) Start() {
    for _, worker := range pool.Workers {
        worker.Start()
    }

    go func() {
        for {
            select {
            case task := <-pool.Tasks:
                go func(task Task) {
                    worker := <-pool.Workers
                    worker.Tasks <- task
                    pool.Workers <- worker
                }(task)
            }
        }
    }()
}

func (pool WorkerPool) Stop() {
    for _, worker := range pool.Workers {
        worker.Stop()
    }

    for _, worker := range pool.Workers {
        <-worker.Quit
    }

    go func() {
        pool.Completion <- true
    }()
}

Теперь можно использовать Worker Pool следующим образом:

func main() {
    pool := NewWorkerPool(5)

    pool.Start()

    for i := 0; i < 10; i++ {
        task := Task{ID: i}
        pool.Tasks <- task
    }

    pool.Stop()

    <-pool.Completion

    fmt.Println("Все задачи выполнены")
}

В этом примере мы создаем пул из 5 рабочих, запускаем его и отправляем в очередь 10 задач. Затем останавливаем пул и ожидаем, пока все задачи будут выполнены. После этого выводим сообщение о завершении работы.

Таким образом, мы организовали структуру Worker Pool в Go, которая позволяет выполнять асинхронные задачи с ограничением одновременно выполняющихся задач. Это улучшает производительность и эффективность при обработке больших объемов работы.