Что такое семафор в Go, для чего он нужен и чем отличается от Mutex и Worker Pool?

Ответ

Семафор — это примитив синхронизации, который позволяет ограничить количество горутин, одновременно выполняющих определенный участок кода или получающих доступ к определенному ресурсу. Его можно представить как счетчик, который отслеживает количество свободных "слотов".

В Go стандартная реализация семафора находится в пакете golang.org/x/sync/semaphore.

Основное назначение:

Контроль параллелизма (concurrency) для предотвращения перегрузки внешних систем (например, API с ограничением по частоте запросов, база данных с лимитом соединений) или для ограничения потребления ресурсов (памяти, CPU).

Пример использования:

Допустим, нам нужно обработать 100 задач, но мы не хотим нагружать систему и хотим выполнять не более 5 задач одновременно.

package main

import (
    "context"
    "fmt"
    "sync"
    "time"

    "golang.org/x/sync/semaphore"
)

func main() {
    // Создаем семафор с "весом" 5, т.е. не более 5 горутин одновременно
    sem := semaphore.NewWeighted(5)
    ctx := context.Background()
    var wg sync.WaitGroup

    for i := 0; i < 20; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()

            // Пытаемся "захватить" один слот. Если все заняты, горутина блокируется.
            if err := sem.Acquire(ctx, 1); err != nil {
                fmt.Printf("Failed to acquire semaphore: %vn", err)
                return
            }
            // По окончании работы "освобождаем" слот
            defer sem.Release(1)

            fmt.Printf("Задача %d: выполняется работа...n", id)
            time.Sleep(1 * time.Second)
            fmt.Printf("Задача %d: работа завершена.n", id)
        }(i)
    }

    wg.Wait()
}

Сравнение с другими примитивами:

  • Семафор vs. Mutex:

    • Mutex — это, по сути, бинарный семафор (с весом 1). Он предоставляет эксклюзивный доступ к ресурсу (либо занято, либо свободно).
    • Семафор позволяет получить доступ к ресурсу N горутинам одновременно.
  • Семафор vs. Worker Pool:

    • Worker Pool — это заранее созданный пул из N горутин, которые ожидают задачи из канала. Количество горутин фиксировано.
    • Семафор не создает горутины. Он лишь ограничивает количество уже запущенных горутин, которые могут войти в критическую секцию. Это более гибкий подход, если горутины создаются динамически для выполнения короткоживущих задач.