← Назад к вопросам

Какие знаешь события с горутинами?

1.7 Middle🔥 202 комментариев
#Основы Go

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

События и механизмы синхронизации горутин в Go

В Go существует несколько ключевых механизмов для синхронизации и координации горутин, которые часто называют "событиями" в контексте параллельного программирования. Вот основные из них:

1. Каналы (Channels)

Каналы - это типизированные "трубы" для передачи данных между горутинами, реализующие модель коммуникации последовательных процессов (CSP).

// Пример использования канала для синхронизации
ch := make(chan int)

go func() {
    // Выполняем работу
    result := 42
    ch <- result // Отправляем результат
}()

value := <-ch // Ждем получения результата
fmt.Println(value) // 42

2. WaitGroup из пакета sync

WaitGroup позволяет ожидать завершения группы горутин.

var wg sync.WaitGroup

for i := 0; i < 3; i++ {
    wg.Add(1)
    go func(id int) {
        defer wg.Done()
        // Работа горутины
        fmt.Printf("Горутина %d завершена\n", id)
    }(i)
}

wg.Wait() // Блокируется, пока все горутины не завершатся
fmt.Println("Все горутины завершены")

3. Мьютексы и RWMutex

Мьютексы обеспечивают взаимное исключение для доступа к общим ресурсам.

var mu sync.Mutex
var counter int

go func() {
    mu.Lock()
    counter++
    mu.Unlock()
}()

// RWMutex позволяет множественное чтение, но эксклюзивную запись
var rwMu sync.RWMutex
go func() {
    rwMu.RLock() // Множественные читатели могут блокироваться одновременно
    // Чтение данных
    rwMu.RUnlock()
}()

4. Cond (Условные переменные)

Cond позволяет горутинам ждать или объявлять о наступлении события.

var mu sync.Mutex
cond := sync.NewCond(&mu)
var ready bool

// Горутина-ожидатель
go func() {
    mu.Lock()
    for !ready {
        cond.Wait() // Освобождает мьютекс и ждет сигнала
    }
    mu.Unlock()
    fmt.Println("Событие произошло!")
}()

// Горутина-инициатор
go func() {
    time.Sleep(time.Second)
    mu.Lock()
    ready = true
    mu.Unlock()
    cond.Signal() // Пробуждает одну ожидающую горутину
    // cond.Broadcast() // Пробуждает все ожидающие горутины
}()

5. Once

Once гарантирует, что функция выполнится ровно один раз, даже если вызвана из нескольких горутин.

var once sync.Once
var initialized bool

initFunc := func() {
    initialized = true
    fmt.Println("Инициализация выполнена")
}

// Множественные горутины могут вызывать, но выполнится только один раз
for i := 0; i < 5; i++ {
    go func() {
        once.Do(initFunc)
    }()
}

6. Context

Context позволяет передавать значения, дедлайны и сигналы отмены между горутинами.

ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()

go func(ctx context.Context) {
    select {
    case <-time.After(3 * time.Second):
        fmt.Println("Работа завершена")
    case <-ctx.Done():
        fmt.Println("Отменено по таймауту:", ctx.Err())
    }
}(ctx)

7. Timer и Ticker

Эти механизмы позволяют создавать события по времени.

// Timer - одноразовое событие
timer := time.NewTimer(2 * time.Second)
<-timer.C
fmt.Println("Таймер сработал")

// Ticker - периодические события
ticker := time.NewTicker(1 * time.Second)
for i := 0; i < 3; i++ {
    <-ticker.C
    fmt.Println("Тик", i)
}
ticker.Stop()

8. Atomic операции

Атомарные операции предоставляют низкоуровневые примитивы для безопасного доступа к памяти.

var count int32

go func() {
    atomic.AddInt32(&count, 1)
}()

value := atomic.LoadInt32(&count)

9. Select с каналами

Конструкция select позволяет ждать нескольких операций с каналами.

ch1 := make(chan string)
ch2 := make(chan string)

go func() { ch1 <- "из канала 1" }()
go func() { ch2 <- "из канала 2" }()

select {
case msg1 := <-ch1:
    fmt.Println(msg1)
case msg2 := <-ch2:
    fmt.Println(msg2)
case <-time.After(time.Second):
    fmt.Println("Таймаут")
}

Практические рекомендации по использованию:

  • Каналы идеальны для передачи данных и координации
  • WaitGroup лучше всего подходит для ожидания завершения группы горутин
  • Мьютексы используйте для защиты общих данных при сложных операциях
  • Context стал стандартом для отмены операций и передачи значений
  • Once незаменим для однократной инициализации
  • Atomic операции эффективны для простых счетчиков и флагов

Выбор конкретного механизма зависит от задачи:

  • Для передачи данных → каналы
  • Для ожидания завершения → WaitGroup
  • Для отмены операций → context
  • Для защиты данных → мьютексы
  • Для ожидания условия → Cond

Каждый из этих механизмов решает определенный класс проблем синхронизации и позволяет создавать корректные, эффективные и читаемые конкурентные программы на Go.

Какие знаешь события с горутинами? | PrepBro