Функция sync.NewCond

Функция sync.NewCond создает новую условную переменную типа *sync.Cond, которая используется для синхронизации горутин при работе с общими ресурсами. В качестве единственного параметра функция принимает интерфейс sync.Locker - обычно это мьютекс типа sync.Mutex или sync.RWMutex. Возвращаемое значение - указатель на структуру sync.Cond, которая предоставляет методы для ожидания и уведомления горутин об изменении состояния.

Синтаксис

gosync.NewCond(l sync.Locker) *sync.Cond

Пример

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

gopackage main

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

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

  // Горутина, которая ждет изменения состояния
  go func() {
    mu.Lock()
    for !ready {
      cond.Wait()
    }
    fmt.Println("Condition met, proceeding...")
    mu.Unlock()
  }()

  // Горутина, которая меняет состояние
  go func() {
    time.Sleep(2 * time.Second)
    mu.Lock()
    ready = true
    cond.Signal()
    fmt.Println("Signal sent")
    mu.Unlock()
  }()

  time.Sleep(3 * time.Second)
}

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

go"Signal sent"
"Condition met, proceeding..."

Пример

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

gopackage main

import (
  "fmt"
  "sync"
)

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

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

  // Увеличиваем счетчик и отправляем широковещательный сигнал
  mu.Lock()
  count = 10
  cond.Broadcast()
  mu.Unlock()

  wg.Wait()
}

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

go"Goroutine 2: count is 10"
"Goroutine 0: count is 10"
"Goroutine 1: count is 10"

Пример

Давайте создадим условную переменную с мьютексом для реализации паттерна "производитель-потребитель":

gopackage main

import (
  "fmt"
  "sync"
)

func main() {
  var mu sync.Mutex
  cond := sync.NewCond(&mu)
  queue := make([]int, 0, 5)
  var wg sync.WaitGroup

  // Потребитель
  wg.Add(1)
  go func() {
    defer wg.Done()
    mu.Lock()
    for len(queue) == 0 {
      cond.Wait()
    }
    item := queue[0]
    queue = queue[1:]
    fmt.Printf("Consumed: %d\n", item)
    mu.Unlock()
  }()

  // Производитель
  mu.Lock()
  queue = append(queue, 42)
  cond.Signal()
  fmt.Println("Produced: 42")
  mu.Unlock()

  wg.Wait()
}

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

go"Produced: 42"
"Consumed: 42"

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

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