Créé par l'IAAmélioré par les gens

Riqli · documents vivants · mis à jour en continu

Là où le savoir de l'IA rencontre la pratique humaine.

Partager avec des amis
image

Конкурентность в Go (goroutines, channels, context)

sections

Введение в конкурентность Go

contents

Аннотация. Данный модуль посвящён ключевым примитивам конкурентного программирования в языке Go: горутинам, каналам и пакету context. Конкурентность — это одна изdefining особенностей Go, позволяющая эффективно использовать многоядерные процессоры и создавать масштабируемые приложения. В отличие от традиционных потоков операционной системы, горутины управляются средой выполнения Go и обладают значительно меньшими накладными расходами.

Цель. Сформировать практические навыки написания безопасных и эффективных конкурентных программ на Go с использованием горутин, каналов и контекста для управления временем жизни операций.

Результаты обучения. После изучения модуля вы сможете: запускать горутины с помощью ключевого слова go; создавать небуферизованные и буферизованные каналы; использовать select для мультиплексирования операций; применять sync.WaitGroup для ожидания завершения группы горутин; управлять отменой и таймаутами через context.WithCancel, WithTimeout и WithDeadline; избегать утечек горутин и взаимных блокировок.

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

sections

Горутины: лёгкие потоки выполнения

contents

Что такое горутина. Горутина — это функция, выполняющаяся конкурентно с вызывающей её функцией. Для запуска горутины используется ключевое слово go перед вызовом функции или анонимной функции. Например, go worker(id, jobs) запускает функцию worker в новой горутине. Аргументы вычисляются в текущей горутине, а выполнение worker происходит в новой .

Лёгкость горутин. Горутины начинаются с минимального стека размером около 2 КБ, который динамически растёт по мере необходимости. Для сравнения, системные потоки обычно требуют 1 МБ начального стека. Управление горутинами осуществляется средой выполнения Go, а не операционной системой, что обеспечивает быстрое переключение контекста и позволяет запускать сотни тысяч горутин на одном сервере без чрезмерного потребления памяти .

Планирование. Go использует модель планирования GMP (Goroutine, M — поток ОС, P — логический процессор). Планировщик распределяет горутины по доступным потокам ОС. Если горутина выполняет блокирующий системный вызов, среда выполнения автоматически переключает другие горутины на другие потоки ОС, чтобы они не простаивали .

Завершение горутин. Горутина завершается естественным образом, когда её функция возвращает управление. Однако если горутина запущена как долгоживущий фоновый процесс, необходимо предусмотреть механизм её остановки. Неуправляемые горутины, ожидающие данные из канала, который никогда не будет закрыт, приводят к утечке памяти .


questions

Какое ключевое слово используется для запуска функции в новой горутине?

réponsesCorrect

go

explications

Ключевое слово go перед вызовом функции запускает её в новой горутине, которая выполняется конкурентно с вызывающей .

réponsesIncorrect

async

réponsesIncorrect

thread

réponsesIncorrect

concurrent


questions

Какой начальный размер стека у горутины в Go?

réponsesCorrect

Около 2 КБ

explications

Горутины начинаются с минимального стека около 2 КБ, который растёт динамически по мере необходимости .

réponsesIncorrect

1 МБ

réponsesIncorrect

8 КБ

réponsesIncorrect

64 КБ

sections

Каналы: коммуникация между горутинами

contents

Назначение каналов. Каналы — это типизированные конвейеры, через которые горутины обмениваются значениями. Философия Go: «Не общайтесь, разделяя память; вместо этого делитесь памятью, общаясь» . Канал создаётся с помощью make(chan T) для небуферизованного или make(chan T, N) для буферизованного канала .

Операции отправки и получения. Отправка значения в канал: ch <- v. Получение значения: v := <-ch. Данные текут в направлении стрелки. Небуферизованный канал блокирует отправителя до тех пор, пока получатель не будет готов принять значение, и наоборот .

Буферизованные каналы. Буферизованный канал имеет ёмкость. Отправка блокируется только когда буфер заполнен. Получение блокируется только когда буфер пуст. Буферизация позволяет отправителю опережать получателя на N элементов и используется для сглаживания пиковых нагрузок, но не должна заменять продуманное проектирование потоков данных .

Закрытие каналов. Отправитель может закрыть канал вызовом close(ch), сигнализируя, что больше значений не будет. Получатели могут проверять закрытие через двухзначную форму: v, ok := <-ch. Если ok == false, канал закрыт и пуст. Цикл for v := range ch автоматически завершается при закрытии канала. Закрывать канал должен только отправитель. Отправка в закрытый канал вызывает панику .

Направленные каналы. Параметры каналов могут быть ограничены только отправкой (chan<- T) или только получением (<-chan T), что документирует намерения и проверяется компилятором .


questions

Что произойдёт при отправке значения в закрытый канал?

réponsesCorrect

Возникнет паника (panic)

explications

Отправка в закрытый канал вызывает панику, поэтому закрывать канал должен только отправитель и только один раз .

réponsesIncorrect

Значение будет проигнорировано

réponsesIncorrect

Канал автоматически откроется снова

réponsesIncorrect

Получатель получит нулевое значение


questions

Какой оператор используется для отправки значения 42 в канал ch?

réponsesCorrect

ch <- 42

explications

Оператор <- направляет значение в канал: ch <- 42 отправляет 42 в канал ch .

réponsesIncorrect

42 -> ch

réponsesIncorrect

send(ch, 42)

réponsesIncorrect

ch = 42

sections

Select: мультиплексирование каналов

contents

Назначение select. Оператор select позволяет горутине ожидать одновременно несколько операций с каналами. Он блокируется до тех пор, пока один из его case-блоков не станет готов к выполнению, после чего выполняет этот case. Если готовы несколько case-блоков, выбирается случайный, что обеспечивает справедливость .

Синтаксис. select похож на switch, но вместо значений проверяет готовность операций с каналами. Каждый case содержит операцию отправки или получения. Например: select { case v := <-ch1: ... case ch2 <- x: ... case <-time.After(time.Second): ... } .

Default. Ветка default выполняется, если ни один из case-блоков не готов. Это позволяет реализовать неблокирующие операции try-send и try-receive. Без default оператор select блокируется, пока хотя бы один канал не будет готов .

Практические сценарии. Select используется для: таймаутов (через time.After), отмены операций (через <-ctx.Done()), неблокирующей отправки или получения, а также для ожидания нескольких источников данных одновременно .


questions

Какая ветка select выполняется, если ни один из case-блоков не готов?

réponsesCorrect

default

explications

Ветка default в select выполняется, когда ни один из case-блоков не готов к выполнению, что делает select неблокирующим .

réponsesIncorrect

Первый case по порядку

réponsesIncorrect

Последний case по порядку

réponsesIncorrect

Возникнет паника

sections

Синхронизация с sync.WaitGroup

contents

Назначение WaitGroup. Пакет sync предоставляет примитив WaitGroup для ожидания завершения группы горутин. Нулевое значение WaitGroup готово к использованию без дополнительной инициализации .

Методы. Add(delta int) увеличивает счётчик на delta. Обычно вызывается перед запуском каждой горутины. Done() уменьшает счётчик на единицу. Эквивалентно Add(-1). Вызывается по завершении горутины, часто через defer. Wait() блокирует вызывающую горутину до тех пор, пока счётчик не станет равным нулю .

Пример использования. Типичный паттерн: var wg sync.WaitGroup, затем для каждой задачи wg.Add(1) и go func() { defer wg.Done(); ... }(). После запуска всех горутин вызывается wg.Wait(), который блокирует основную горутину до завершения всех задач. Этот механизм особенно полезен, когда главная горутина должна дождаться завершения рабочих горутин, прежде чем продолжить .


questions

Какой метод sync.WaitGroup уменьшает счётчик на единицу?

réponsesCorrect

Done()

explications

Метод Done() уменьшает счётчик WaitGroup на единицу и обычно вызывается через defer при завершении горутины .

réponsesIncorrect

Wait()

réponsesIncorrect

Add(-1)

réponsesIncorrect

Close()

sections

Пакет context: управление временем жизни

contents

Назначение context. Пакет context предоставляет механизм передачи сигналов отмены, дедлайнов и таймаутов через границы API и между горутинами. Контекст позволяет управлять временем жизни операций и предотвращать утечки горутин .

Создание контекста. context.Background() возвращает пустой корневой контекст, обычно используемый в main или на верхнем уровне. context.TODO() используется, когда родительский контекст ещё не определён, но в будущем должен быть добавлен .

WithCancel. context.WithCancel(parent) возвращает производный контекст и функцию cancel. Вызов cancel() закрывает канал Done(), сигнализируя всем горутинам, слушающим этот контекст, о необходимости завершиться. Отмена освобождает ресурсы, поэтому cancel следует вызывать как можно скорее после завершения операции .

WithTimeout и WithDeadline. context.WithTimeout(parent, duration) создаёт контекст, который автоматически отменяется через указанный интервал. context.WithDeadline(parent, time) устанавливает абсолютное время дедлайна. Оба возвращают функцию cancel для досрочной отмены .

Паттерн использования. Контекст передаётся первым аргументом в функции, выполняющие потенциально длительные операции. Горутина периодически проверяет <-ctx.Done() в select и завершается при получении сигнала. Это позволяет реализовать graceful shutdown и таймауты для сетевых запросов, операций ввода-вывода и фоновых задач .


questions

Какая функция пакета context возвращает производный контекст с автоматической отменой через заданный интервал времени?

réponsesCorrect

context.WithTimeout