AIが作成人が改善

Riqli · ライブドキュメント · 常に更新

AIの知識と人間の実践が出会う場所。

友達と共有
image

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

セクション

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

contents

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

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

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

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

セクション

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

contents

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

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

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

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


questions

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

answers正解

go

explanations

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

answers不正解

async

answers不正解

thread

answers不正解

concurrent


questions

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

answers正解

Около 2 КБ

explanations

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

answers不正解

1 МБ

answers不正解

8 КБ

answers不正解

64 КБ

セクション

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

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

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

answers正解

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

explanations

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

answers不正解

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

answers不正解

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

answers不正解

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


questions

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

answers正解

ch <- 42

explanations

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

answers不正解

42 -> ch

answers不正解

send(ch, 42)

answers不正解

ch = 42

セクション

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-блоков не готов?

answers正解

default

explanations

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

answers不正解

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

answers不正解

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

answers不正解

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

セクション

Синхронизация с 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 уменьшает счётчик на единицу?

answers正解

Done()

explanations

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

answers不正解

Wait()

answers不正解

Add(-1)

answers不正解

Close()

セクション

Пакет 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 возвращает производный контекст с автоматической отменой через заданный интервал времени?

answers正解

context.WithTimeout