> Можно ли шарить данные между потоками (Go)
Уровень: senior · Роль: backend · Язык: Go · Категория: Технические вопросы
Компании: Avito
Стек: Go
> Пример ответа
Короткий ответ
Да, в Go данные между потоками (горутинами) шарить можно, но идиоматичный подход - не разделять память, а коммуницировать через каналы: "Do not communicate by sharing memory; instead, share memory by communicating". Однако на практике разделяемая память тоже используется - через мьютексы, атомики или sync.Map. Главное - соблюдать правила синхронизации, чтобы избежать гонок данных (data races). Выбор зависит от сценария: каналы для потоков данных и событий, мьютексы для защиты критических секций, атомики для простых счётчиков.
Подробное объяснение
В Go "потоки" - это горутины, которые выполняются конкурентно на пуле OS-потоков. Шарить данные между ними можно двумя основными способами:
-
Через каналы - передача владения данными. Горутина-отправитель передаёт значение, получатель забирает его. Это безопасно, потому что в каждый момент времени доступ к данным имеет только одна горутина. Каналы бывают буферизированные и небуферизированные, работают по принципу FIFO.
-
Через разделяемую память с синхронизацией - несколько горутин читают и пишут в одну переменную, но доступ контролируется:
sync.Mutex/sync.RWMutex- для сложных структур и критических секцийsync/atomic- для простых числовых операций (счётчики, флаги)sync.Map- для конкурентного чтения/записи в map (оптимизирован под сценарии, где ключи стабильны, а значения часто читаются)
Важно понимать trade-off: каналы дают более высокоуровневую абстракцию и упрощают рассуждение о корректности, но добавляют накладные расходы на копирование и синхронизацию. Мьютексы эффективнее для частого доступа к большим структурам, но требуют дисциплины и повышают риск deadlock'ов.
Go-рантайм предоставляет детектор гонок (-race), который помогает находить проблемы при тестировании. На уровне языка нет защиты от гонок - это ответственность разработчика.
На практике
Для senior-позиции важно показать понимание, когда какой инструмент выбирать:
- Каналы - когда данные передаются от производителя к потребителю, есть поток событий, или нужно организовать pipeline. Особенно если логика естественно ложится на модель "отправил-получил".
- Mutex - когда несколько горутин работают с одной структурой, и операции не сводятся к простому обмену сообщениями (например, обновление нескольких полей структуры).
- Atomic - для счётчиков, флагов, прогресс-баров, где нужна максимальная производительность и нет сложной логики.
- sync.Map - когда ключи в map почти не меняются, но значения читаются очень часто из многих горутин.
На практике часто комбинируют: например, канал для управления жизненным циклом, мьютекс для защиты состояния, атомики для статистики.
Пример кода
GO// Пример с каналом - передача данныхfunc worker(ch <-chan int, wg *sync.WaitGroup) {defer wg.Done()for v := range ch {fmt.Println("got:", v)}}// Пример с мьютексом - защита счётчикаtype Counter struct {mu sync.Mutexvalue int}func (c *Counter) Inc() {c.mu.Lock()defer c.mu.Unlock()c.value++}func (c *Counter) Value() int {c.mu.Lock()defer c.mu.Unlock()return c.value}// Пример с атомиком - быстрый счётчикvar atomicCounter int64atomic.AddInt64(&atomicCounter, 1)val := atomic.LoadInt64(&atomicCounter)
Как отвечать на собеседовании
Начните с чёткого "да, можно", затем упомяните два подхода: каналы и разделяемую память с синхронизацией. Подчеркните, что идиоматичный путь - каналы, но на практике нужны все инструменты. Приведите конкретные сценарии: каналы для pipeline, мьютексы для сложных структур, атомики для счётчиков. Обязательно упомяните детектор гонок и правило "не разделяй память, а передавай данные". Если спросят про производительность - скажите, что атомики быстрее мьютексов, а каналы медленнее, но безопаснее по дизайну. Покажите, что понимаете trade-off между простотой и скоростью.
Что проверяет интервьюер
Интервьюер оценивает:
- Понимание модели конкурентности в Go (горутины, а не потоки OS)
- Знание идиом: каналы как основной механизм, а не просто "ещё один способ"
- Умение выбирать инструмент под задачу, а не использовать один везде
- Понимание проблем: data race, deadlock, starvation
- Знание sync-пакета и atomic-операций
- Практический опыт: когда что реально применяется в продакшене
Типичные ошибки
- Ответ "да, через глобальные переменные" без упоминания синхронизации - сразу красный флаг
- Утверждение, что каналы - единственный правильный способ, и мьютексы не нужны
- Игнорирование детектора гонок и тестирования с
-race - Непонимание разницы между буферизированным и небуферизированным каналом
- Предложение использовать
sync.Mapдля всех случаев - это узкоспециализированный инструмент - Забывают про
sync.RWMutexдля сценариев "много читателей, мало писателей"
> Похожие задачи по Go
Как обезопасить взаимодействие микросервисов при использовании REST или gRPC
Что нужно помнить при добавлении элементов в слайс Go без знания точного размера
Как спроектировать систему url shortener на уровне микросервисов
Как уведомить горутины в Go о необходимости завершения при получении первого результата
> Похожие задачи по backend
Как обезопасить взаимодействие микросервисов при использовании REST или gRPC
Что нужно помнить при добавлении элементов в слайс Go без знания точного размера
Как спроектировать систему url shortener на уровне микросервисов
Как уведомить горутины в Go о необходимости завершения при получении первого результата
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью