Назад к вопросам
Middle
151
questionbank

Что такое мьютекс, какие типы мьютексов существуют и как их использовать?

Sobes Copilot

Получайте ответы в реальном времени

Ответ от нейросети

sobes.tech AI

Мьютекс (от англ. MUTual EXclusion) — это примитив синхронизации, используемый для обеспечения исключительного доступа к разделяемому ресурсу между конкурирующими горутинами. Он гарантирует, что в любой момент времени только одна горутина может владеть мьютексом и, соответственно, обращаться к защищаемым данным.

В Go пакет sync предоставляет два основных типа мьютексов:

  • sync.Mutex: Стандартный мьютекс. Предоставляет методы Lock() и Unlock(). Если мьютекс уже заблокирован, вызов Lock() блокирует текущую горутину до тех пор, пока он не будет освобожден. Unlock() разблокирует мьютекс.

    var mu sync.Mutex
    var counter int
    
    func increment() {
        mu.Lock() // Блокируем доступ к counter
        counter++
        mu.Unlock() // Разблокируем доступ
    }
    
  • sync.RWMutex: Мьютекс для чтения/записи. Позволяет множеству горутин одновременно читать защищаемый ресурс, но только одной горутине писать. Предоставляет методы Lock(), Unlock() (для записи), RLock() и RUnlock() (для чтения).

    var rwMu sync.RWMutex
    var data []int
    
    func readData() {
        rwMu.RLock() // Блокируем для чтения
        // Чтение data
        fmt.Println(data)
        rwMu.RUnlock() // Разблокируем чтение
    }
    
    func writeData(val int) {
        rwMu.Lock() // Блокируем для записи
        // Запись в data
        data = append(data, val)
        rwMu.Unlock() // Разблокируем запись
    }
    

Использование мьютексов необходимо для предотвращения состояний гонки (race conditions), когда несколько горутин одновременно пытаются модифицировать одни и те же данные, приводя к непредсказуемым результатам.

Правила использования:

  1. Вызывать Lock() перед доступом к защищаемому ресурсу.
  2. Вызывать Unlock() после завершения доступа. Рекомендуется использовать defer mu.Unlock() для гарантии разблокировки даже в случае паники.
  3. Не пытаться блокировать уже заблокированный мьютекс из той же горутины (может привести к дедлоку).
  4. Не пытаться разблокировать незаблокированный мьютекс.
  5. При использовании RWMutex, использовать RLock/RUnlock для операций чтения и Lock/Unlock для операций записи.
  6. Мьютексы не должны копироваться после первого использования. Передавайте их по ссылке или указателю.

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

func safeIncrement() {
    mu.Lock()
    defer mu.Unlock() // Гарантирует разблокировку
    counter++
}