Черновик: страница не показывается в списке статей и закрыта от поисковиков. Чтобы опубликовать, убери draft: true и пересобери.
  1. Главная
  2. Статьи
  3. Каналы или мьютексы: что выбрать в Go
Статья · Go

Каналы или мьютексы: что выбрать в Go

Когда синхронизация через каналы делает код проще, а когда только прячет гонки. Правило выбора, примеры и типичные ошибки.

Артём Романович 4 мин чтения
Содержание
  1. Правило в одну строку
  2. Когда нужен мьютекс
  3. Когда нужны каналы
  4. Типичные ошибки
  5. Чек-лист выбора

«Не общайтесь через разделяемую память — разделяйте память через общение». Эту фразу из Go Proverbs помнят все, и из-за неё каналы тащат туда, где хватило бы одного sync.Mutex. Код становится длиннее, а гонки никуда не деваются.

Разберём простое правило выбора. Этот вопрос часто задают на собесах в формате «а почему здесь не канал?».

Правило в одну строку

Мьютекс защищает состояние. Канал передаёт владение данными или сигнал от одной горутины другой.

Если несколько горутин читают и меняют одну структуру (кэш, счётчик, map с сессиями), нужен мьютекс. Если данные текут по этапам и в каждый момент у них один хозяин, лучше подходят каналы.

ЗадачаВыборПочему
Кэш в памятиsync.RWMutexОбщее состояние, много чтений
Счётчик запросовatomic.Int64Одна операция, мьютекс избыточен
Пайплайн обработкиканалыДанные переходят из рук в руки
Сигнал «стоп»context / close(ch)Широковещательное событие
Пул воркеровканал задачОчередь с естественным backpressure

Когда нужен мьютекс

Классика — кэш. Состояние живёт в одной структуре, методы короткие, критическая секция видна глазами.

cache.go
type Cache struct {
	mu    sync.RWMutex
	items map[string]Item
}
 
func (c *Cache) Get(key string) (Item, bool) {
	c.mu.RLock()
	defer c.mu.RUnlock()
	it, ok := c.items[key]
	return it, ok
}
 
func (c *Cache) Set(key string, it Item) {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.items[key] = it
}

Тот же кэш на каналах потребует отдельной горутины-владельца, типа запроса с каналом ответа и аккуратной остановки. Кода втрое больше, а каждое чтение превращается в два переключения горутин.

Бесплатные материалы в TelegramРазборы задач с собесов, конспекты и вакансии для Go-разработчиков. Подписаться →

Когда нужны каналы

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

pipeline.go
func parse(ctx context.Context, lines <-chan string) <-chan Event {
	out := make(chan Event)
	go func() {
		defer close(out)
		for line := range lines {
			ev, err := decode(line)
			if err != nil {
				continue
			}
			select {
			case out <- ev:
			case <-ctx.Done():
				return
			}
		}
	}()
	return out
}

Обрати внимание на select с ctx.Done(): без него при отмене горутина навсегда зависнет на отправке, если читатель уже ушёл. Это самая частая утечка горутин.

Типичные ошибки

ОшибкаЧто будетКак поймать
Запись в map из двух горутин без мьютексаfatal error: concurrent map writes, процесс падает без recovergo test -race
Скопировали структуру с sync.MutexКопия и оригинал блокируются независимо, защиты нетgo vet (copylocks)
Повторный Lock в той же горутинеДедлок: мьютекс в Go не реентерабельныйКод-ревью, таймауты в тестах
Отправка в канал, который никто не читаетУтечка горутиныpprof goroutine, goleak в тестах
close канала со стороны читателяpanic: send on closed channel у писателяЗакрывает только отправитель

Чек-лист выбора

  • Есть общая структура, которую читают и меняют? Мьютекс.
  • Меняется одно число? sync/atomic.
  • Данные переходят между этапами? Каналы.
  • Нужно разослать «стоп» всем? context или close(ch).
  • Мьютекс держится дольше пары строк или вокруг I/O? Пересмотри дизайн, а не примитив.
Разберём твой уровень за 20 минутБесплатный созвон: оценю, чего не хватает до Middle, и составлю план. Без обязательств.
Бесплатная консультация

Формат работы

Выбери подходящий формат — от прожарки резюме до полного сопровождения до оффера.

Составление резюме

Адаптирую твой опыт, оформим достижения, добавлю ключевые фразы под ATS — робота, который отсеивает резюме до того, как их увидит живой рекрутер. Будем улучшать до результата.

19 000 ₽

Разовая услуга

  • Адаптация опыта под вакансии
  • ATS-оптимизация
  • Улучшаем резюме, пока HR не начинают писать каждый день
Записаться

Бесплатная консультация

Не уверен, сработаемся ли? Запишись на 20-минутный созвон — бесплатно проконсультирую и отвечу на все вопросы.

Бесплатно

20 минут созвон

  • Оценка текущего уровня
  • Рекомендации по развитию
  • Ответы на все вопросы
Записаться

Бесплатная консультация — 20 мин

Обсудим твой уровень, цели и составим предварительный план развития. Без обязательств.

1

Напиши мне

2

Созвон 20 мин

3

Получи план

Записаться в Telegram