Метод sync.Cond.Broadcast

Метод sync.Cond.Broadcast пробуждает все горутины, которые ожидают на данном условном объекте. В отличие от метода Signal, который пробуждает только одну горутину, Broadcast пробуждает все ожидающие горутины одновременно. Этот метод используется в ситуациях, когда изменение состояния затрагивает всех ожидающих, и все они должны продолжить выполнение.

Синтаксис

goc.Broadcast()

Метод не принимает параметров и не возвращает значений. Он вызывается на экземпляре sync.Cond после изменения общего состояния, которое ожидают горутины.

Пример

Давайте рассмотрим базовый пример использования Broadcast для пробуждения всех ожидающих горутин:

gopackage main

import (
  "fmt"
  "sync"
  "time"
)

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

  // Запускаем три горутины, которые ожидают сигнала
  for i := 1; i <= 3; i++ {
    go func(id int) {
      mu.Lock()
      for !ready {
        cond.Wait()
      }
      fmt.Printf("Goroutine %d: started\n", id)
      mu.Unlock()
    }(i)
  }

  // Даем горутинам время начать ожидание
  time.Sleep(100 * time.Millisecond)

  // Меняем состояние и пробуждаем все горутины
  mu.Lock()
  ready = true
  cond.Broadcast()
  mu.Unlock()

  // Даем время на вывод результатов
  time.Sleep(200 * time.Millisecond)
}

Результат выполнения кода:

go"Goroutine 3: started"
"Goroutine 1: started"
"Goroutine 2: started"

В этом примере все три горутины ожидают изменения переменной ready. После вызова Broadcast все они пробуждаются и продолжают выполнение. Порядок вывода может отличаться.

Пример

Давайте сравним поведение Broadcast и Signal на практическом примере:

gopackage main

import (
  "fmt"
  "sync"
  "time"
)

func main() {
  var mu sync.Mutex
  cond := sync.NewCond(&mu)
  var dataReady bool
  var wg sync.WaitGroup

  // Запускаем 5 горутин, ожидающих данные
  for i := 1; i <= 5; i++ {
    wg.Add(1)
    go func(id int) {
      defer wg.Done()
      mu.Lock()
      for !dataReady {
        cond.Wait()
      }
      fmt.Printf("Worker %d: processing data\n", id)
      mu.Unlock()
    }(i)
  }

  time.Sleep(100 * time.Millisecond)

  // Подготавливаем данные и пробуждаем всех
  mu.Lock()
  dataReady = true
  fmt.Println("Main: data ready, broadcasting...")
  cond.Broadcast()
  mu.Unlock()

  wg.Wait()
}

Результат выполнения кода:

go"Main: data ready, broadcasting..."
"Worker 5: processing data"
"Worker 2: processing data"
"Worker 1: processing data"
"Worker 3: processing data"
"Worker 4: processing data"

В этом примере все пять воркеров ожидают готовности данных. Использование Broadcast позволяет запустить их всех одновременно. Если бы мы использовали Signal, то пробудилась бы только одна горутина.

Пример

Давайте рассмотрим пример с циклическим пробуждением всех горутин на каждом этапе:

gopackage main

import (
  "fmt"
  "sync"
  "time"
)

func main() {
  var mu sync.Mutex
  cond := sync.NewCond(&mu)
  stage := 0
  var wg sync.WaitGroup

  // Запускаем горутины, ожидающие этапов
  for i := 1; i <= 3; i++ {
    wg.Add(1)
    go func(id int) {
      defer wg.Done()
      for stage < 3 {
        mu.Lock()
        for stage == 0 {
          cond.Wait()
        }
        fmt.Printf("Worker %d: stage %d completed\n", id, stage)
        mu.Unlock()
      }
      fmt.Printf("Worker %d: all stages done\n", id)
    }(i)
  }

  // Последовательно проходим этапы
  for s := 1; s <= 3; s++ {
    time.Sleep(200 * time.Millisecond)
    mu.Lock()
    stage = s
    fmt.Printf("Main: stage %d ready\n", s)
    cond.Broadcast()
    mu.Unlock()
  }

  wg.Wait()
}

Результат выполнения кода:

go"Main: stage 1 ready"
"Worker 3: stage 1 completed"
"Worker 1: stage 1 completed"
"Worker 2: stage 1 completed"
"Main: stage 2 ready"
"Worker 2: stage 2 completed"
"Worker 1: stage 2 completed"
"Worker 3: stage 2 completed"
"Main: stage 3 ready"
"Worker 3: stage 3 completed"
"Worker 2: stage 3 completed"
"Worker 1: stage 3 completed"
"Worker 1: all stages done"
"Worker 2: all stages done"
"Worker 3: all stages done"

В этом примере Broadcast используется на каждом этапе для пробуждения всех горутин, которые затем обрабатывают текущий этап и снова переходят в ожидание.

Смотрите также

  • метод sync.Cond.Signal,
    который пробуждает одну ожидающую горутину
  • метод sync.Cond.Wait,
    который переводит горутину в режим ожидания
  • структуру sync.Cond,
    которая представляет условную переменную
  • метод sync.NewCond,
    который создает новый условный объект