Что такое `sync.RWMutex` и в чем его отличие от `sync.Mutex`?

Ответ

sync.RWMutex (Read-Write Mutex) — это примитив синхронизации в Go, который обеспечивает более гранулярный контроль доступа к общим данным по сравнению с обычным sync.Mutex.

Ключевое отличие:

  • sync.Mutex: Предоставляет эксклюзивную блокировку. В любой момент времени только одна горутина может владеть блокировкой, независимо от того, читает она данные или пишет.
  • sync.RWMutex: Разделяет блокировки на чтение и запись. Он позволяет неограниченному числу горутин одновременно читать данные, но только одной горутине — писать. При этом во время записи чтение также невозможно.

Когда использовать RWMutex?

RWMutex наиболее эффективен в сценариях, где количество операций чтения значительно превышает количество операций записи. В таких случаях он позволяет достичь большей производительности за счет параллельного выполнения читающих горутин.

Основные методы:

  • RLock() / RUnlock(): Устанавливают и снимают блокировку на чтение (разделяемую).
  • Lock() / Unlock(): Устанавливают и снимают блокировку на запись (эксклюзивную).

Пример:

package main

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

// Config представляет собой общую конфигурацию, которую часто читают и редко обновляют
type Config struct {
    data  map[string]string
    mutex sync.RWMutex
}

// Get получает значение по ключу (безопасно для чтения)
func (c *Config) Get(key string) string {
    c.mutex.RLock() // Устанавливаем блокировку на чтение
    defer c.mutex.RUnlock()
    return c.data[key]
}

// Set устанавливает значение (безопасно для записи)
func (c *Config) Set(key, value string) {
    c.mutex.Lock() // Устанавливаем эксклюзивную блокировку на запись
    defer c.mutex.Unlock()
    c.data[key] = value
}

Важное правило: Попытка повысить блокировку с чтения (RLock) до записи (Lock) внутри одной горутины приведет к дедлоку. Нельзя вызывать Lock(), не сняв RLock().