Go Backend — Вопросы на собеседовании

Вопросы по рантайму Go, конкурентности (Goroutines, Channels), планировщику (G-M-P), работе с памятью и сборке мусора.

Что такое горутины (Goroutines) и чем они отличаются от потоков ОС?

Горутина — это легковесный поток управления, управляемый рантаймом Go, а не операционной системой. * **Размер стека**: Поток ОС имеет фиксированный стек большого размера (обычно 1–8 МБ). Горутина создается с динамическим стеком размером всего 2 КБ, который растет и сжимается по мере необходимости. * **Управление**: Потоки ОС планируются ядром системы, переключение контекста требует системного вызова и сохранения регистров процессора (дорого). Горутины планируются внутренним планировщиком Go в пространстве пользователя (дешево). * **Масштабируемость**: В приложении можно одновременно запустить сотни тысяч горутин, в то время как лимит потоков ОС измеряется тысячами.

Как устроен планировщик Go (модель G-M-P)?

Планировщик Go использует модель M:N распределения горутин на потоки ОС с помощью трех сущностей: * **G (Goroutine)**: Горутина. Описывает стек, состояние и исполняемый код. * **M (Machine)**: Физический поток операционной системы, создаваемый рантаймом. * **P (Processor)**: Локальный контекст планировщика (процессор). Представляет ресурс, необходимый для выполнения кода G на потоке M. Количество P обычно совпадает с количеством ядер процессора (`GOMAXPROCS`). * **Принцип работы**: У каждого P есть локальная очередь готовых горутин. M связывается с P для выполнения G из его очереди. Если очередь пуста, P пытается украсть (work stealing) половину горутин у другого P или берет их из глобальной очереди.

Разница между буферизованными и небуферизованными каналами?

Канал (channel) — это примитив синхронизации и обмена данными между горутинами: * **Небуферизованный канал** (по умолчанию): Блокирует отправителя (`ch <- x`) до тех пор, пока получатель не выполнит чтение (`<-ch`), и наоборот. Это обеспечивает гарантированную синхронизацию двух горутин в точке обмена. * **Буферизованный канал** (`make(chan T, capacity)`): Имеет внутренний кольцевой буфер. Отправитель не блокируется при записи, пока буфер не заполнен. Получатель не блокируется при чтении, пока в буфере есть данные.

Что происходит при чтении, записи или закрытии закрытого или nil канала?

Поведение каналов в крайних состояниях: * **`nil` канал** (не инициализирован через `make`): * Чтение (`<-ch`) -> бесконечная блокировка горутины. * Запись (`ch <- x`) -> бесконечная блокировка горутины. * Закрытие (`close(ch)`) -> паника (panic). * **Закрытый канал**: * Чтение (`<-ch`) -> возвращает дефолтное значение типа (zero-value) и флаг готовности `false` (например, `val, ok := <-ch`). Не блокирует. * Запись (`ch <- x`) -> немедленная паника (panic). * Закрытие (`close(ch)`) -> немедленная паника (panic).

В чем разница между слайсом (slice) и массивом (array) в Go?

* **Массив (Array)**: Тип значения с фиксированной длиной, заданной на этапе компиляции (например, `[5]int`). Длина является частью типа. При передаче массива в функцию происходит его полное копирование. * **Слайс (Slice)**: Динамическое представление над нижележащим массивом. Является структурой из трех полей: 1. Указатель на первый элемент базового массива (`array pointer`). 2. Длина слайса (`len`). 3. Вместимость базового массива от начала слайса (`cap`). При передаче слайса копируется только эта структура-заголовок, а не сами элементы.

Как работает функция append в Go? Как увеличивается capacity?

Функция `append` добавляет элементы в конец слайса: 1. Проверяется, достаточно ли текущего `cap` для размещения новых элементов. 2. Если места достаточно, `append` изменяет существующий базовый массив, увеличивает `len` и возвращает новый слайс. 3. Если `cap` превышен, рантайм выделяет новый массив большего размера, копирует туда старые элементы, добавляет новые и возвращает новый слайс. 4. **Алгоритм роста**: До 256 элементов емкость обычно удваивается. При больших размерах емкость увеличивается примерно на 25% (множитель `1.25`) за одну аллокацию для плавного роста.

В чем разница между интерфейсами interface{} (any) в Go?

Начиная с Go 1.18, `any` является встроенным синонимом (alias) для пустого интерфейса `interface{}`. * **Суть**: Пустой интерфейс не описывает ни одного метода, поэтому любой тип данных в Go автоматически удовлетворяет ему. * **Рантайм**: В рантайме интерфейс представляется структурой `eface` (empty interface), которая содержит два указателя: 1. Указатель на информацию о типе (`_type`). 2. Указатель на сами данные (`data`).

Как устроен Garbage Collector в Go?

Сборщик мусора Go оптимизирован для минимизации задержек (низкий latency) и работает параллельно с кодом приложения. * **Алгоритм**: Используется трехцветный алгоритм разметки и очистки (Tri-color Mark-and-Sweep) с барьером записи (Write Barrier). * **Белый цвет**: Кандидаты на удаление. * **Серый цвет**: Обнаруженные объекты, чьи связи еще не проверены. * **Черный цвет**: Живые объекты, чьи связи проверены. Не удаляются. * **Write Barrier**: Перехватывает изменения ссылок во время работы сборщика, чтобы живые объекты случайно не остались белыми из-за параллельной мутации данных кодом приложения. Паузы Stop-The-World в Go обычно длятся микросекунды.

Что такое механизм убегания памяти на кучу (Escape Analysis) в Go?

Escape Analysis (анализ утечек) — это алгоритм компилятора, который решает, где выделить память под создаваемую переменную: в стеке (дешево) или в куче (дорого, требует GC). * **Правило**: Если компилятор видит, что ссылка на переменную переживает область видимости функции (например, возвращается указатель из функции), переменная «убегает» в кучу. * **Другие причины убегания**: * Передача данных в пустой интерфейс `interface{}` / `any`. * Выделение памяти под объекты неопределенного на этапе компиляции размера (большие слайсы). * Отправка указателей через каналы.

Как работает планировщик Go при системных вызовах (Syscalls)?

Когда горутина выполняет блокирующий системный вызов (например, чтение файла): 1. Рантайм Go перехватывает это действие. Поток ОС M, на котором выполняется горутина G, блокируется. 2. Планировщик отпускает контекст P (система переходит в состояние `sysmon` детекции) и связывает свободный или новый поток M с P для выполнения других горутин из очереди (механизм handoff). 3. Когда системный вызов завершается, горутина G пытается захватить свободный контекст P для продолжения выполнения. Если все P заняты, горутина кладется в глобальную очередь, а поток M засыпает.

В чем разница между передачей по значению и по указателю в Go?

В Go **все параметры всегда передаются по значению** (копируются). * **Передача переменной**: Копируется все содержимое структуры или примитива. * **Передача указателя**: Копируется адрес области памяти. Это позволяет изменять исходный объект внутри функции и экономит память на копировании больших структур. * **Встроенные типы**: Слайсы, мапы и каналы под капотом содержат указатели на внутренние структуры данных, поэтому при передаче их «по значению» изменения их содержимого видны вызывающей стороне.

Что такое defer? Каков порядок выполнения нескольких defer?

Ключевое слово `defer` откладывает выполнение функции до момента выхода из окружающей функции. Аргументы функции вычисляются немедленно в точке объявления `defer`. * **Порядок выполнения**: Несколько вызовов `defer` выполняются по принципу LIFO (Last-In, First-Out) — последний объявленный defer сработает первым.

Как устроена структура данных map в Go?

`map` в Go реализована как хэш-таблица. * ** buckets**: Состоит из массива бакетов (`bmap`). Каждый бакет хранит до 8 пар «ключ-значение» и массив хэш-кодов (tophash) для быстрого поиска. * **Эвакуация**: При росте таблицы (когда коэффициент заполнения превышает 6.5) выделяется массив бакетов в 2 раза большего размера, и данные постепенно переносятся (эвакуируются) из старых бакетов в новые во время операций записи или удаления.

Что такое panic и recover в Go? Как правильно обрабатывать ошибки?

* **`panic`**: Прерывает обычный ход выполнения программы, запускает выполнение всех отложенных функций (`defer`) вверх по стеку вызовов. Если паника не перехвачена, приложение падает. * **`recover`**: Встроенная функция, которая позволяет перехватить панику и восстановить работу программы. Вызывается **только** внутри функций, объявленных в `defer`. * **Обработка ошибок**: В Go ошибки — это обычные значения, реализующие интерфейс `error`. Паника должна использоваться только для критических сбоев приложения (out of memory, index out of range), а обычные ошибки должны возвращаться явно вторым аргументом.

В чем разница между пакетами sync.Mutex и sync.RWMutex?

* **`sync.Mutex`**: Взаимное исключение. Блокирует критическую секцию для всех. Допускает только один поток выполнения в любой момент времени (и для чтения, и для записи). * **`sync.RWMutex`**: Блокировка чтения/записи. Разделяет доступ: допускает множественное одновременное чтение (читатели не блокируют друг друга), но требует эксклюзивного доступа для записи (блокирует всех). Полезно для структур с частым чтением и редкой записью.

Что такое пакет sync.WaitGroup и как его использовать?

`sync.WaitGroup` используется для ожидания завершения группы горутин. * **Методы**: * `Add(delta int)`: Увеличивает счетчик активных задач. * `Done()`: Уменьшает счетчик на 1 (вызывается в конце работы горутины, обычно через `defer`). * `Wait()`: Блокирует текущую горутину до тех пор, пока счетчик не станет равным 0.

Что такое контекст (context.Context) в Go и зачем он нужен?

`context.Context` используется для управления жизненным циклом асинхронных операций, передачи сигналов отмены, таймаутов и метаданных запроса по цепочке вызовов горутин. * **Основные типы**: * `context.WithCancel`: Сигнал ручной отмены операции. * `context.WithTimeout` / `WithDeadline`: Автоматическая отмена по истечении времени. * `context.WithValue`: Передача сквозных метаданных (например, ID запроса).

В чем разница между методами init() и main() в Go?

* **`main()`**: Точка входа в приложение (может быть только одна в пакете `main`). * **`init()`**: Функция инициализации пакета. Вызывается автоматически при импорте пакета до функции `main()`. В одном пакете (и даже файле) может быть объявлено несколько функций `init()`, они будут выполнены по очереди их объявления.

Что такое пустые структуры struct{} и где они полезны?

Пустая структура `struct{}` не занимает места в памяти (`0` байт). **Применение**: * Сигналы в каналах (например, `chan struct{}` для уведомления о завершении работы, так как передача значения не тратит память). * Реализация множества (Set) на основе мапы: `map[key]struct{}`. Значение ключа мапы не тратит память.

Как работают интерфейсы под капотом в Go? Разница между eface и iface?

Интерфейсы в Go типизируются динамически в рантайме с помощью двух структур: * **`eface`** (empty interface): Представляет пустой интерфейс `interface{}`. Содержит тип `_type` и сами данные `data`. * **`iface`** (non-empty interface): Представляет интерфейс с методами. Содержит структуру `itab` (которая хранит информацию об интерфейсе, конкретном типе объекта и таблицу указателей на методы этого типа) и сами данные `data`.

Что такое race detector в Go и как его включить?

Race detector — это инструмент рантайма для обнаружения гонок за данные (когда несколько горутин обращаются к одной переменной без синхронизации, и как минимум одно обращение является записью). * **Включение**: Запускается с помощью флага `-race` при тестировании или сборке (`go test -race` или `go run -race main.go`). Встраивает в бинарный код проверки обращений к памяти, увеличивая потребление ОЗУ и CPU.

Разница между пакетами log и log/slog в Go?

* **`log`**: Классический логер. Выводит неструктурированный текст в стандартный поток вывода. * **`log/slog`**: Структурированный логер, появившийся в Go 1.21. Позволяет выводить логи в формате JSON или парах «ключ-значение» (key-value) с поддержкой уровней логирования, хэндлеров и контекстов, что критично для современных систем мониторинга.

Что делает функция select в Go?

Оператор `select` позволяет горутине ожидать операции ввода-вывода сразу по нескольким каналам. Выполняется тот блок `case`, канал которого готов к обмену первым. Если готовы несколько — выбирается случайный. Если не готов ни один и объявлен блок `default` — управление передается в него (неблокирующий опрос).

Что такое утечки памяти в Go и как pprof помогает их искать?

Утечка памяти в Go возникает, когда объекты в куче остаются доступными для корневых ссылок (например, сохранены в глобальной мапе или заблокированной горутине), поэтому Garbage Collector не может их удалить. * **pprof**: Встроенный инструмент профилирования. Позволяет делать срезы использования памяти (heap profile) во времени, анализировать аллокации и находить функции, которые выделяют память неэффективно.

Как устроена работа с горутинами-воркерами (Worker Pool pattern)?

Паттерн Worker Pool ограничивает количество параллельно работающих горутин для обработки задач: 1. Создается пул из N горутин-воркеров. 2. Воркеры в бесконечном цикле читают задачи из общего канала задач `jobs`. 3. Главный поток отправляет задачи в канал `jobs` и закрывает его по окончании. 4. Результаты работы воркеры отправляют в канал `results`.

В чем отличие sync.Pool от обычного кэша?

`sync.Pool` — это временный кэш объектов, предназначенный исключительно для оптимизации работы аллокатора памяти. Объекты в пуле могут быть автоматически удалены сборщиком мусора в любой момент без предупреждения. Обычный кэш гарантирует сохранность данных, а sync.Pool используется для переиспользования выделенной памяти под временные структуры (например, буферы).

Что такое Shadowing (затенение) переменных в Go?

Затенение возникает, когда переменная с тем же именем объявляется во внутреннем блоке кода (например, внутри `if` или `for`), перекрывая (затеняя) переменную внешнего блока. Это частый источник логических ошибок, особенно при использовании оператора быстрого объявления `:=`.

Как безопасно скопировать мапу (map) в Go?

Встроенного метода копирования мап в Go нет. Так как мапа является ссылочным типом, простое присвоение `map2 = map1` скопирует только ссылку. Для создания независимой копии необходимо создать новую мапу через `make` и в цикле скопировать все ключи и значения вручную.

Что такое интерфейсы-заглушки (Mocking) и как они помогают в тестах в Go?

В Go интерфейсы реализуются неявно (duck typing). Это позволяет легко подменять реальные зависимости (например, клиент БД) в модульных тестах на заглушки (mock), реализующие те же интерфейсы, изолируя тестируемый код от внешних систем.

Как устроен механизм graceful shutdown для веб-сервера в Go?

Graceful shutdown позволяет серверу корректно завершить обработку текущих запросов перед остановкой: 1. Сервер запускается в фоновой горутине. 2. Главный поток слушает системные сигналы завершения (`SIGINT`, `SIGTERM`) через канал. 3. При получении сигнала вызывается метод `server.Shutdown(ctx)`, который закрывает входящие соединения и ожидает завершения активных обработчиков в рамках таймаута контекста.

Как устроен планировщик Go (модель G-M-P)?

Планировщик Go маппит N горутин на M потоков ОС с помощью трех сущностей: * **G (Goroutine)**: Горутина. Описывает стек, состояние выполнения и указатель на код. * **M (Machine)**: Физический поток операционной системы, выполняющий код. * **P (Processor)**: Ресурс (логический процессор), необходимый для выполнения G. Количество P по умолчанию равно числу ядер процессора. Каждый P имеет локальную очередь готовых к запуску горутин. M захватывает P и выполняет G из его локальной очереди. Если локальная очередь пуста, M пытается украсть (work stealing) задачи из очередей других P или берет их из глобальной очереди.

Что происходит при создании горутины (ключевое слово go)?

Когда выполняется инструкция `go func()`: 1. Создается новая структура `G` (или берется из пула свободных горутин для экономии аллокаций). 2. Выделяется стек минимального размера (в Go 1.4+ это 2 КБ). Стек горутины динамический и может расти/сжиматься. 3. Инициализируются регистры и параметры запуска. 4. Горутина помещается в локальную очередь выполнения (`runq`) текущего процессора `P`. 5. Если очередь переполнена, горутина отправляется в глобальную очередь выполнения.

Как устроен канал (channel) под капотом в Go?

Канал в Go — это указатель на структуру `hchan` в куче, защищенную мьютексом: * **Буфер**: Циклический буфер-массив (`buf`) с указателями начала и конца для хранения элементов (в буферизованных каналах). * **Очередь получателей (`recvq`)**: Двусвязный список горутин (структур `sudog`), заблокированных на чтение из пустого канала. * **Очередь отправителей (`sendq`)**: Список горутин, заблокированных на запись в заполненный канал. * **Потокобезопасность**: Запись и чтение защищены внутренним lock-мьютексом структуры `hchan`.

Разница между sync.Mutex и sync.RWMutex в Go?

* **`sync.Mutex`**: Взаимное исключение (Mutual Exclusion). Только один поток/горутина может захватить блокировку в любой момент времени, блокируя как запись, так и чтение. * **`sync.RWMutex`**: Блокировка чтения-записи (Reader-Writer Mutex). Позволяет множеству горутин параллельно захватывать блокировку на чтение (`RLock`), если нет пишущей горутины. Но только одна горутина может захватить блокировку на запись (`Lock`), полностью перекрывая доступ читателям и писателям. Оптимизирует скорость при преобладании чтений.

Как устроен и для чего нужен sync.Pool в Go?

`sync.Pool` — это механизм кэширования временных объектов в памяти для повторного использования, снижающий нагрузку на аллокатор кучи и сборщик мусора (GC). * **Принцип**: Объект извлекается методом `Get()` (если пула пуст, вызывается функция генерации `New`) и возвращается методом `Put()`. * **Особенность**: Объекты в `sync.Pool` могут быть удалены сборщиком мусора в любой момент во время очередного цикла GC, поэтому пул не подходит для хранения соединений или кэша долговечных данных.

Как устроен сборщик мусора (GC) в Go?

Сборщик мусора в Go является конкурентным, не перемещающим (non-moving) и построен на основе алгоритма **Mark & Sweep** с использованием **трицветной разметки (Tri-color marking)**: * **Белые**: Кандидаты на удаление. * **Серые**: Достижимые объекты, чьи связи с другими объектами еще не исследованы. * **Черные**: Достижимые объекты, связи которых проверены. * **Write Barrier (барьер записи)**: Специальный код компилятора, отслеживающий создание новых связей во время работы GC, чтобы новые ссылки не были удалены сборщиком случайно. GC в Go минимизирует паузы Stop-The-World (обычно менее 1 мс).

Как устроена хэш-карта (map) под капотом в Go?

Карта в Go реализована как хэш-таблица на основе структуры `hmap`: * **Бакеты (Buckets)**: Массив бакетов (`bmap`). Каждый бакет хранит до 8 пар «ключ-значение» и хэш-коды. * **Коллизии**: Решаются с помощью связывания бакетов через указатель на переполненный бакет (`overflow`). * **Масштабирование (Evacuation)**: При росте коэффициента заполнения карта увеличивается в 2 раза. Перенос данных (эвакуация) происходит лениво (постепенно) во время выполнения последующих операций записи/удаления, чтобы не вызывать пиковых задержек.

Как устроен слайс (slice) в Go и как работает append?

Слайс в Go — это заголовок (структура), содержащая три поля: 1. Указатель на первый элемент базового массива. 2. Длина слайса (`len`). 3. Вместимость базового массива (`cap`). * **`append`**: Добавляет элемент в слайс. Если `len < cap`, элемент просто записывается в базовый массив, а длина `len` увеличивается. Если `len == cap`, Go выделяет новый массив большего размера (обычно в 2 раза больше на малых размерах, далее с коэффициентом 1.25), копирует туда старые данные и перенаправляет указатель слайса.

Как устроены интерфейсы (eface, iface) в Go?

Интерфейсы в Go под капотом состоят из двух указателей: * **`eface`** (пустой интерфейс `interface{}`): Состоит из указателя на тип данных (`_type`) и указателя на сами данные (`data`). * **`iface`** (интерфейс с методами): Состоит из указателя на интерфейсную таблицу (`itab`) и указателя на данные (`data`). `itab` содержит метаданные о типе, типе самого интерфейса и список указателей на методы для быстрого вызова. Проверка соответствия интерфейсу выполняется в рантайме.

Как оптимизирован вызов defer в современных версиях Go?

В ранних версиях Go вызов `defer` приводил к аллокации структуры в куче, что делало его дорогим. * Начиная с Go 1.13, введена оптимизация **стековых деферов**: структуры `_defer` выделяются на стеке горутины, что ускорило работу. * В Go 1.14 введена оптимизация **open-coded defers** (открытые деферы): если количество вызовов defer известно на этапе компиляции, компилятор разворачивает их прямо в коде функции перед точками выхода, исключая накладные расходы рантайма.

В чем разница между передачей по значению и по ссылке (указателю) в Go?

В Go все аргументы передаются **по значению** (копируются). * При передаче переменной копируется само значение. * При передаче указателя (`*T`) копируется адрес памяти (указатель). Это позволяет изменять исходный объект внутри функции. * **Важно**: Слайсы, мапы и каналы под капотом являются структурами с указателями, поэтому их передача ведет себя как передача по ссылке (изменения элементов слайса будут видны снаружи, но изменение длины самого слайса — нет).

Что такое Escape Analysis в Go?

Escape Analysis (анализ побега) — это алгоритм компилятора, определяющий, где выделить память под переменную: на стеке или в куче. * **Стек**: Выделение происходит очень быстро, память очищается автоматически при выходе из функции. Переменная остается на стеке, если компилятор гарантирует, что ссылка на нее не выйдет за пределы жизненного цикла функции. * **Куча**: Если ссылка на переменную возвращается из функции (убегает), переменная выделяется в куче, а ее очисткой займется GC.

Зачем нужен context.Context в Go?

`context.Context` используется для управления временем жизни асинхронных операций, передачи сигналов отмены, таймаутов и метаданных по всей цепочке вызовов (особенно при обработке HTTP-запросов): * **Отмена операций**: Метод `WithCancel` возвращает контекст и функцию отмены. При ее вызове закрывается канал `Done()`, уведомляя горутины о необходимости прекратить работу. * **Таймауты**: `WithTimeout` / `WithDeadline` автоматически отменяют операции по истечении времени.

Как работают panic и recover в Go?

* **`panic`**: Прерывает нормальный поток выполнения функции. Запускает раскрутку стека (unwinding), поочередно выполняя все отложенные (`defer`) вызовы в текущей горутине, после чего приложение завершается аварийно. * **`recover`**: Встроенная функция, которая позволяет перехватить панику и остановить падение приложения. Вызывается исключительно внутри `defer` блока. Если паники не было, `recover()` возвращает `nil`.

Как устроены Generics (обобщения) в Go?

Дженерики в Go (введенные в 1.18) позволяют писать функции и структуры с параметризованными типами с использованием ограничений типов (Constraints): * **Реализация**: Go использует гибридный подход. Для простых типов компилятор выполняет мономорфизацию (генерацию копий функций под конкретные типы при сборке). Для более сложных типов используется передача словарей типов в рантайме, что позволяет избежать сильного раздувания бинарного файла.

Что такое race detector в Go и как он работает?

Race Detector — инструмент для поиска состояний гонки (Data Races) во время выполнения тестов или работы приложения. Запускается флагом `-race`. * **Как устроен**: Компилятор инструментирует каждую операцию чтения и записи переменных в коде, добавляя вызовы к рантайму детектора. Детектор отслеживает историю обращений потоков к ячейкам памяти с помощью алгоритма векторных часов и логирует пересечения несинхронизированного доступа из разных горутин.

Когда оправдано использовать пакет unsafe в Go?

Пакет `unsafe` позволяет обходить систему типизации Go и работать с указателями напрямую. **Применение**: * Низкоуровневая оптимизация производительности (например, быстрое преобразование слайса байт в строку без выделения памяти и копирования: `unsafe.String(&b[0], len(b))`). * Интеграция с кодом на C (Cgo). * **Риски**: Код теряет переносимость, стабильность и может сломаться при любом обновлении версии Go.

Как отлаживать утечки памяти в Go с помощью pprof?

1. Подключить пакет `net/http/pprof` в приложение. 2. Собрать профиль использования кучи: `go tool pprof http://localhost:6060/debug/pprof/heap`. 3. Использовать команду `top` для выявления функций, выделяющих больше всего памяти. 4. Команда `list <имя_функции>` показывает аллокации построчно. 5. Сравнение профилей (диффы): получить два профиля с временным интервалом и сравнить их с помощью флага `-base`, чтобы увидеть, какие объекты накапливаются и не удаляются GC.

Что делают директивы компилятора go:noescape и go:linkname?

* **`//go:noescape`**: Указывается перед сигнатурой функции без тела (написанной на ассемблере). Запрещает Escape Analysis отправлять аргументы этой функции в кучу, гарантируя их удержание на стеке. * **`//go:linkname`**: Позволяет связать локальное имя функции или переменной с приватным символом из другого пакета (даже стандартной библиотеки), обходя правила видимости Go. Часто используется в низкоуровневых системных библиотеках.

В чем суть Go Memory Model?

Go Memory Model определяет условия, при которых чтение переменной в одной горутине гарантированно видит запись в ту же переменную в другой горутине. Для этого используются примитивы синхронизации. Основное правило: без явной синхронизации (каналы, `sync.Mutex`, `sync/atomic`) порядок чтения и записи в параллельных горутинах не определен.

Назад ко всем разделам · Посмотреть тарифы Hinterly