sync.Mutex.Lock

Метод sync.Mutex.Lock блокирует мьютекс. Если мьютекс уже заблокирован, вызов метода блокирует выполнение текущей горутины до тех пор, пока мьютекс не будет освобожден. После успешной блокировки, горутина получает монопольный доступ к критической секции кода. Метод не принимает никаких параметров и не возвращает значений.

Синтаксис

gomu.Lock()

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

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

gopackage main

import (
  "fmt"
  "sync"
)

func main() {
  var mu sync.Mutex
  counter := 0

  var wg sync.WaitGroup
  for i := 0; i < 1000; i++ {
    wg.Add(1)
    go func() {
      defer wg.Done()
      mu.Lock()
      counter++
      mu.Unlock()
    }()
  }

  wg.Wait()
  fmt.Println(counter)
}

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

go1000

Пример с блокировкой доступа к структуре

Рассмотрим использование мьютекса для защиты доступа к полям структуры:

gopackage main

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

type SafeCounter struct {
  mu  sync.Mutex
  value int
}

func (c *SafeCounter) Increment() {
  c.mu.Lock()
  c.value++
  c.mu.Unlock()
}

func (c *SafeCounter) GetValue() int {
  c.mu.Lock()
  defer c.mu.Unlock()
  return c.value
}

func main() {
  counter := &SafeCounter{}

  for i := 0; i < 5; i++ {
    go func() {
      counter.Increment()
    }()
  }

  time.Sleep(time.Millisecond)
  fmt.Println(counter.GetValue())
}

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

go5

Пример взаимной блокировки

Важно правильно использовать метод Lock, чтобы избежать взаимных блокировок. Рассмотрим некорректный пример:

gopackage main

import (
  "fmt"
  "sync"
)

func main() {
  var mu1, mu2 sync.Mutex

  go func() {
    mu1.Lock()
    mu2.Lock()
    fmt.Println("Горутина 1 захватила оба мьютекса")
    mu2.Unlock()
    mu1.Unlock()
  }()

  go func() {
    mu2.Lock()
    mu1.Lock()
    fmt.Println("Горутина 2 захватила оба мьютекса")
    mu1.Unlock()
    mu2.Unlock()
  }()
}

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

go"fatal error: all goroutines are asleep - deadlock!"

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

Рекомендуется использовать defer для гарантированного освобождения мьютекса:

gopackage main

import (
  "fmt"
  "sync"
)

func main() {
  var mu sync.Mutex
  data := make(map[int]int)

  var wg sync.WaitGroup
  for i := 0; i < 10; i++ {
    wg.Add(1)
    go func(id int) {
      defer wg.Done()
      mu.Lock()
      defer mu.Unlock()
      data[id] = id * id
    }(i)
  }

  wg.Wait()
  mu.Lock()
  fmt.Println(data)
  mu.Unlock()
}

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

gomap[0:0 1:1 2:4 3:9 4:16 5:25 6:36 7:49 8:64 9:81]

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

  • метод sync.Mutex.Unlock,
    который освобождает заблокированный мьютекс
  • метод sync.Mutex.TryLock,
    который пытается заблокировать мьютекс без ожидания
  • тип sync.RWMutex,
    который предоставляет более гибкие механизмы блокировки
  • тип sync.WaitGroup,
    который используется для ожидания завершения группы горутин