TGViewer
Библиотека Go-разработчика | Golang Библиотека Go-разработчика | Golang @goproglib · 24.1K subscribers
Post #7106 3.38K
⚡️ Нельзя присваивать sync.Mutex между переменными

sync.Mutex не просто структура с двумя полями. Это объект с состоянием, и если вы присвоите один мьютекс другому во время работы горутин, получите либо панику, либо гонку данных.

Что происходит при присвоении

Посмотрите на этот код:
var mu1 sync.Mutex
var mu2 sync.Mutex

// Горутина 1: захватывает mu1
go func() {
mu1.Lock()
defer mu1.Unlock()
// работа...
}()

// Горутина 2: заменяет mu1 на mu2
go func() {
mu1 = mu2 // замена мьютекса прямо во время использования
}()


Здесь два опасных сценария.

Сценарий 1: паника

Горутина 1 захватила mu1. Горутина 2 присвоила mu1 = mu2. Теперь mu1 — это чистый, незахваченный мьютекс. Когда горутина 1 дойдёт до Unlock(), она попытается разблокировать мьютекс, который никто не блокировал, и получит панику:

unlock of unlocked mutex


Присвоение переменных не создаёт отношения happens-before между горутинами, в отличие от запуска и завершения горутины. Поэтому порядок, в котором горутина 1 увидит изменение mu1, непредсказуем.

Сценарий 2: гонка данных

Если горутина 1 разблокирует mu1 в тот же момент, когда горутина 2 делает присвоение, обе одновременно записывают в поля state и sema внутри структуры sync.Mutex. Это классическая гонка данных. Итог — повреждение состояния одного или обоих мьютексов.

Как делать правильно

Не присваивайте мьютексы напрямую. Если нужно передать мьютекс в другое место, используйте указатель:
var mu sync.Mutex

// Передаём указатель, а не копию
go func(m *sync.Mutex) {
m.Lock()
defer m.Unlock()
// работа...
}(&mu)


Это же правило распространяется на структуры, которые содержат sync.Mutex. Копировать их тоже нельзя. Только передавать по указателю:
type SafeCounter struct {
mu sync.Mutex
count int
}

// Правильно
func increment(c *SafeCounter) {
c.mu.Lock()
defer c.mu.Unlock()
c.count++
}

// Неправильно: мьютекс скопируется вместе со структурой
func incrementWrong(c SafeCounter) {
c.mu.Lock()
defer c.mu.Unlock()
c.count++
}


Инструмент go vet умеет находить такие копирования и выдаёт предупреждение copylocks.

sync.Mutex нельзя копировать или присваивать после первого использования. Передавайте мьютексы и структуры с ними только по указателю.

📍 Навигация: Вакансии • Задачи • Собесы

🐸 Библиотека Go-разработчика

#GoDeep
  • ❤ 13
  • 🥱 1
More from @goproglib
  1. Sep 30, 2026🧑‍💻 Эмулятор AWS-сервисов Kumo — это небольшой инструмент на Go для локальной имитации A…
  2. Sep 29, 2026👨‍💻 Библиотека для написания LSP-серверов Написать свой Language Server с нуля на Go сло…
  3. Sep 28, 2026🤔 Вопрос с собеседования по Go Что выведет программа? ❤️ — 1 true / 0 false 🔥 — 1 true /…
  4. Sep 28, 2026👩‍💻 Что на самом деле происходит внутри Go map? После Go 1.24 обычный map внутри работае…
  5. Sep 26, 2026🔥 В Go 1.27 появился portable SIMD До этого SIMD-оптимизации в Go требовали архитектурног…
  6. Sep 25, 2026🤡🤡 📍 Навигация: Вакансии • Задачи • Собесы 🐸 Библиотека Go-разработчика #GoGiggle
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →