Создано ШІУлучшено людьми

Riqli · жівые документы · обновляются постоянно

Синергія знань ШІ та практики людей.

Поделіться з друзьями
image

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

разделы

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

содержаніе

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

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

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

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

разделы

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

содержаніе

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

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

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

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


вопросы

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

ответыПравильний

go

объясненія

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

ответыНеправильний

async

ответыНеправильний

thread

ответыНеправильний

concurrent


вопросы

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

ответыПравильний

Около 2 КБ

объясненія

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

ответыНеправильний

1 МБ

ответыНеправильний

8 КБ

ответыНеправильний

64 КБ

разделы

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

содержаніе

Назначение каналов. Каналы — это типизированные конвейеры, через которые горутины обмениваются значениями. Философия 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), что документирует намерения и проверяется компилятором .


вопросы

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

ответыПравильний

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

объясненія

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

ответыНеправильний

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

ответыНеправильний

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

ответыНеправильний

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


вопросы

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

ответыПравильний

ch <- 42

объясненія

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

ответыНеправильний

42 -> ch

ответыНеправильний

send(ch, 42)

ответыНеправильний

ch = 42

разделы

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

содержаніе

Назначение 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()), неблокирующей отправки или получения, а также для ожидания нескольких источников данных одновременно .


вопросы

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

ответыПравильний

default

объясненія

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

ответыНеправильний

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

ответыНеправильний

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

ответыНеправильний

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

разделы

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

содержаніе

Назначение 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(), который блокирует основную горутину до завершения всех задач. Этот механизм особенно полезен, когда главная горутина должна дождаться завершения рабочих горутин, прежде чем продолжить .


вопросы

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

ответыПравильний

Done()

объясненія

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

ответыНеправильний

Wait()

ответыНеправильний

Add(-1)

ответыНеправильний

Close()

разделы

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

содержаніе

Назначение 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 и таймауты для сетевых запросов, операций ввода-вывода и фоновых задач .


вопросы

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

ответыПравильний

context.WithTimeout