Являются ли операции с `sync.Mutex` в Go блокирующими?

Ответ

Да, операции с sync.Mutex в Go являются блокирующими.

Это означает, что когда одна горутина захватывает мьютекс, любая другая горутина, пытающаяся его захватить, будет остановлена (заблокирована) до тех пор, пока мьютекс не будет освобожден.

Основные операции:

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

Пример:

var mu sync.Mutex
var counter int

func increment() {
    mu.Lock()   // Блокировка: горутина ждет, пока мьютекс не освободится
    defer mu.Unlock() // Гарантированное освобождение в конце функции
    counter++
}

Ключевые моменты:

  1. Deadlock (Взаимоблокировка): Попытка повторно заблокировать уже заблокированный той же горутиной мьютекс (mu.Lock() -> mu.Lock()) приведет к вечной блокировке.
  2. Правильная разблокировка: Вызов Unlock() для мьютекса, который не был заблокирован текущей горутиной, вызовет panic. Разблокировать мьютекс должна та же горутина, что его и заблокировала.
  3. sync.RWMutex: Для ситуаций, где есть много читателей и редкие писатели, лучше использовать RWMutex. Он позволяет множеству горутин одновременно читать данные (RLock/RUnlock), но запись (Lock/Unlock) требует эксклюзивного доступа.
  4. TryLock (Go 1.18+): Существует неблокирующий метод mu.TryLock(), который пытается захватить мьютекс и немедленно возвращает true в случае успеха или false, если мьютекс уже занят, не блокируя горутину.