sync.Cond.Signal

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

Синтаксис

goc.Signal()

Пример с одной ожидающей горутиной

Рассмотрим базовый пример, где одна горутина ожидает сигнала, а другая отправляет его:

gopackage main

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

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

  go func() {
    mu.Lock()
    defer mu.Unlock()

    fmt.Println("Горутина ожидает сигнала")
    cond.Wait()
    fmt.Println("Горутина получила сигнал")
  }()

  time.Sleep(100 * time.Millisecond)

  mu.Lock()
  fmt.Println("Отправляем сигнал")
  cond.Signal()
  mu.Unlock()

  time.Sleep(100 * time.Millisecond)
}

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

go"Горутина ожидает сигнала"
"Отправляем сигнал"
"Горутина получила сигнал"

Пример с несколькими ожидающими горутинами

Покажем, что метод Signal пробуждает только одну горутину из нескольких ожидающих:

gopackage main

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

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

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

      mu.Lock()
      defer mu.Unlock()

      fmt.Printf("Горутина %d ожидает\n", id)
      cond.Wait()
      fmt.Printf("Горутина %d проснулась\n", id)
    }(i)
  }

  time.Sleep(100 * time.Millisecond)

  mu.Lock()
  fmt.Println("Отправляем один сигнал")
  cond.Signal()
  mu.Unlock()

  time.Sleep(100 * time.Millisecond)

  mu.Lock()
  fmt.Println("Отправляем второй сигнал")
  cond.Signal()
  mu.Unlock()

  wg.Wait()
}

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

go"Горутина 3 ожидает"
"Горутина 1 ожидает"
"Горутина 2 ожидает"
"Отправляем один сигнал"
"Горутина 3 проснулась"
"Отправляем второй сигнал"
"Горутина 1 проснулась"

Пример с проверкой условия

Покажем использование метода Signal в связке с проверкой условия в цикле:

gopackage main

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

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

  go func() {
    mu.Lock()
    defer mu.Unlock()

    for !ready {
      fmt.Println("Ожидаем готовности")
      cond.Wait()
    }
    fmt.Println("Условие выполнено!")
  }()

  time.Sleep(100 * time.Millisecond)

  mu.Lock()
  fmt.Println("Меняем состояние")
  ready = true
  cond.Signal()
  mu.Unlock()

  time.Sleep(100 * time.Millisecond)
}

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

go"Ожидаем готовности"
"Меняем состояние"
"Условие выполнено!"

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

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