Тип sync.Mutex

Тип sync.Mutex предоставляет механизм взаимного исключения для защиты критических секций в многопоточных программах на Go. Он позволяет синхронизировать доступ к общим ресурсам, гарантируя, что только одна горутина одновременно может выполнять защищенный участок кода. Основные методы мьютекса — это Lock, который блокирует мьютекс, и Unlock, который освобождает блокировку. Мьютекс должен создаваться с помощью ключевого слова var или через new, и его нулевое значение готово к использованию.

Синтаксис

govar mu sync.Mutex
mu.Lock()
// критическая секция
mu.Unlock()

Пример

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

gopackage main

import (
  "fmt"
  "sync"
)

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

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

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

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

go100

Пример

Покажем, что происходит, если не использовать мьютекс. В этом случае счетчик может стать меньше ожидаемого значения из-за состояния гонки:

gopackage main

import (
  "fmt"
  "sync"
)

func main() {
  counter := 0

  var wg sync.WaitGroup
  for i := 0; i < 100; i++ {
    wg.Add(1)
    go func() {
      defer wg.Done()
      counter++ // гонка данных
    }()
  }

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

Результат выполнения кода (может быть разным):

go95

Пример

Мьютекс также можно использовать для защиты структуры данных. В этом примере мы защищаем доступ к карте:

gopackage main

import (
  "fmt"
  "sync"
)

type SafeMap struct {
  mu sync.Mutex
  m  map[string]int
}

func NewSafeMap() *SafeMap {
  return &SafeMap{
    m: make(map[string]int),
  }
}

func (sm *SafeMap) Set(key string, value int) {
  sm.mu.Lock()
  defer sm.mu.Unlock()
  sm.m[key] = value
}

func (sm *SafeMap) Get(key string) int {
  sm.mu.Lock()
  defer sm.mu.Unlock()
  return sm.m[key]
}

func main() {
  safeMap := NewSafeMap()
  var wg sync.WaitGroup

  for i := 0; i < 10; i++ {
    wg.Add(1)
    go func(i int) {
      defer wg.Done()
      key := fmt.Sprintf("key%d", i)
      safeMap.Set(key, i)
    }(i)
  }

  wg.Wait()
  fmt.Println(safeMap.Get("key5"))
}

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

go5

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

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