Golang | Вопросы собесов
4.34K subscribers
26 photos
1 video
713 links
Download Telegram
🤔 Что такое Prometheus?

Prometheus – это мощная система мониторинга с временными рядами (time series), которая собирает метрики из сервисов, хранит их и позволяет строить графики и отправлять алерты.

🚩Ключевые особенности Prometheus

Pull-модель – сам запрашивает метрики у сервисов (в отличие от push-модели, как в StatsD).
Формат временных рядов – каждая метрика привязана ко времени и меткам (labels).
Язык запросов PromQL – позволяет анализировать и агрегировать метрики.
Автодетектирование сервисов – поддержка Kubernetes, Docker, Consul.
Хранение данных в базе TSDB (Time Series Database).
Гибкая система алертов – интеграция с Alertmanager (уведомления в Slack, Telegram и др.).

🚩Как работает Prometheus?

Экспортеры/сервисы предоставляют метрики через HTTP-эндпоинт (/metrics).
Prometheus сам запрашивает данные по расписанию.
Метрики хранятся в базе TSDB.
Можно строить графики в Grafana или запрашивать данные через API.
Алерты отправляются в Alertmanager при достижении пороговых значений.

🚩Пример метрик в Go
Go-сервис может отдавать метрики через HTTP с помощью prometheus/client_golang
package main

import (
"net/http"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)

// Создаём метрику
var httpRequests = prometheus.NewCounter(
prometheus.CounterOpts{
Name: "http_requests_total",
Help: "Total number of HTTP requests",
})

func main() {
// Регистрируем метрику
prometheus.MustRegister(httpRequests)

http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
httpRequests.Inc() // Увеличиваем счётчик при каждом запросе
w.Write([]byte("Hello, Prometheus!"))
})

// Эндпоинт для сбора метрик
http.Handle("/metrics", promhttp.Handler())

http.ListenAndServe(":8080", nil)
}


Пример запроса в PromQL
http_requests_total


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Почему пустой интерфейс можно привести к любому типу?

Пустой интерфейс (interface{} в Go) может содержать значения любого типа, так как не накладывает никаких ограничений на структуру данных. Он используется как универсальный контейнер.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Сколько очередей в паттерне?

Паттерн "Очередь" (Queue) в программировании обычно представляет собой структуру данных или архитектурный подход, используемый для обработки сообщений, задач или запросов в определённом порядке (обычно FIFO — "первым вошёл, первым вышел").
Но если рассматривать количество очередей в каком-то конкретном паттерне проектирования, то всё зависит от контекста. Например:

🚩Одиночная очередь (Single Queue)

Все задачи обрабатываются в одной очереди по порядку. Используется, когда важен строгий порядок выполнения. Пример: канал (channel) в Go, куда отправляются задачи для обработки.

🚩Несколько очередей (Multiple Queues)

Используется, когда разные типы задач требуют разной обработки. Например, очереди с разными приоритетами (высокий, средний, низкий). В многопоточных системах каждая очередь может обслуживаться отдельным worker'ом.

🚩Очередь с пуулом воркеров (Worker Pool Queue)

Есть одна очередь задач, но несколько рабочих (goroutines), которые извлекают задачи и обрабатывают их параллельно.
Уменьшает нагрузку на систему и ускоряет выполнение.
     package main

import (
"fmt"
"sync"
"time"
)

func worker(id int, jobs <-chan int, results chan<- int, wg *sync.WaitGroup) {
defer wg.Done()
for job := range jobs {
fmt.Printf("Worker %d started job %d\n", id, job)
time.Sleep(time.Second) // имитация работы
fmt.Printf("Worker %d finished job %d\n", id, job)
results <- job * 2
}
}

func main() {
jobs := make(chan int, 5)
results := make(chan int, 5)
var wg sync.WaitGroup

numWorkers := 3
for i := 1; i <= numWorkers; i++ {
wg.Add(1)
go worker(i, jobs, results, &wg)
}

for j := 1; j <= 5; j++ {
jobs <- j
}
close(jobs)

wg.Wait()
close(results)

for res := range results {
fmt.Println("Result:", res)
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Как пустой интерфейс связан с обычным?

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

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Что может быть пустым интерфейсом?

Пустой интерфейс interface{} является универсальным контейнером, который может содержать значение любого типа. Это связано с тем, что в Go любой тип автоматически реализует пустой интерфейс, поскольку в нем нет методов, которые нужно реализовать.

🟠Примитивные типы
числа, строки, булевы значения и т.д.
🟠Композитные типы
массивы, срезы, карты, структуры.
🟠Функции
функции различных типов.
🟠Другие интерфейсы
значения, которые реализуют другие интерфейсы.

🚩Примеры

Примитивные типы
package main

import "fmt"

func main() {
var i interface{}
i = 42
fmt.Println(i) // Output: 42

i = "hello"
fmt.Println(i) // Output: hello

i = true
fmt.Println(i) // Output: true
}


Композитные типы
package main

import "fmt"

func main() {
var i interface{}

i = []int{1, 2, 3}
fmt.Println(i) // Output: [1 2 3]

i = map[string]int{"one": 1, "two": 2}
fmt.Println(i) // Output: map[one:1 two:2]

type Person struct {
Name string
Age int
}

i = Person{Name: "Alice", Age: 30}
fmt.Println(i) // Output: {Alice 30}
}


Функции
package main

import "fmt"

func main() {
var i interface{}

i = func() {
fmt.Println("Hello from function")
}

if f, ok := i.(func()); ok {
f() // Output: Hello from function
}
}


Другие интерфейсы
package main

import "fmt"

type Stringer interface {
String() string
}

type Person struct {
Name string
Age int
}

func (p Person) String() string {
return fmt.Sprintf("%s (%d years old)", p.Name, p.Age)
}

func main() {
var i interface{}

i = Person{Name: "Alice", Age: 30}

if str, ok := i.(Stringer); ok {
fmt.Println(str.String()) // Output: Alice (30 years old)
}
}


🚩Проверка и приведение типа

Утверждение типа
package main

import "fmt"

func main() {
var i interface{} = 42

if v, ok := i.(int); ok {
fmt.Println("Integer:", v) // Output: Integer: 42
} else {
fmt.Println("Not an integer")
}
}


Использование switch для проверки типа
package main

import "fmt"

func printType(i interface{}) {
switch v := i.(type) {
case string:
fmt.Println("String:", v)
case int:
fmt.Println("Integer:", v)
case bool:
fmt.Println("Boolean:", v)
default:
fmt.Printf("Unknown type: %T\n", v)
}
}

func main() {
printType("hello") // Output: String: hello
printType(42) // Output: Integer: 42
printType(true) // Output: Boolean: true
printType(3.14) // Output: Unknown type: float64
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Что такое Grafana?

Это инструмент для визуализации метрик и логов. Он позволяет строить графики, панели мониторинга и алерты на основе данных из Prometheus, InfluxDB, Loki и других источников.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Что такое замыкание?

Замыкание (closure) — это функция, которая запоминает и использует переменные из своей внешней области видимости, даже если эта область видимости уже закончила своё выполнение.

🚩Как это работает?

Когда внутренняя функция обращается к переменным внешней функции, она "замыкает" эти переменные на себе. Они продолжают существовать, пока жива сама функция-замыкание.
package main

import "fmt"

func counter() func() int {
count := 0
return func() int {
count++
return count
}
}

func main() {
inc := counter() // создаём замыкание
fmt.Println(inc()) // 1
fmt.Println(inc()) // 2
fmt.Println(inc()) // 3

newInc := counter() // новое замыкание с новой переменной count
fmt.Println(newInc()) // 1
}


🚩Где полезно использовать?

Счётчики (как в примере выше). Фабричные функции, создающие специфичные обработчики. Ограничение доступа к данным (инкапсуляция).

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Зачем нужен Prometheus?

Это система мониторинга и алертинга, разработанная для сбора метрик в реальном времени. Он хранит данные в timeseries формате и предоставляет мощный язык запросов (PromQL) для анализа.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 По какому алгоритму растет slice?

В Go срезы (slice) динамически изменяемы, и при добавлении новых элементов их вместимость (capacity) увеличивается по определённому алгоритму.

🚩Как растёт `slice` в Go?

Когда срезу требуется больше места, чем доступно в его текущей capacity, происходит автоматическое выделение нового массива с увеличенным размером, и элементы копируются в новый массив.

🚩Алгоритм роста:

Если cap(slice) < 1024, то новая ёмкость (capacity) удваивается.
Если cap(slice) >= 1024, то увеличение идёт примерно на 25% от текущего размера.

package main

import "fmt"

func main() {
var s []int
prevCap := cap(s)

for i := 0; i < 20; i++ {
s = append(s, i)
if cap(s) != prevCap {
fmt.Printf("Len: %d, New Cap: %d (growth: %.2fx)\n", len(s), cap(s), float64(cap(s))/float64(prevCap))
prevCap = cap(s)
}
}
}


Выходные данные (может отличаться в зависимости от реализации Go)
Len: 1, New Cap: 1 (growth: Inf)
Len: 2, New Cap: 2 (growth: 2.00x)
Len: 3, New Cap: 4 (growth: 2.00x)
Len: 5, New Cap: 8 (growth: 2.00x)
Len: 9, New Cap: 16 (growth: 2.00x)
Len: 17, New Cap: 32 (growth: 2.00x)


🚩Почему так?

🟠Экономия памяти
если бы рост был на 1 элемент, то это вызывало бы частые копирования.
🟠Скорость работы
экспоненциальный рост уменьшает количество выделений памяти.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Зачем нужна Grafana?

Grafana нужна для гибкой визуализации данных из Prometheus и других хранилищ. Она позволяет:
- Создавать интерактивные панели мониторинга.
- Настраивать уведомления (alerts).
- Подключаться к разным источникам данных.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Знакомы ли с концепцией 12FA для проектирования SaaS приложений?

Да, знаком. 12FA (12-Factor App) — это набор принципов, созданных разработчиками Heroku для построения масштабируемых, надежных и удобных в развертывании SaaS-приложений. Эти принципы особенно полезны при разработке облачных сервисов (Cloud-Native).

🚩Основные 12 факторов

🟠Кодовая база (Codebase)
У приложения должна быть единая кодовая база (один репозиторий), независимо от количества развертываний (production, staging, dev).

🟠Зависимости (Dependencies)
Все зависимости должны явно указываться в go.mod / go.sum (для Go). Никаких глобальных зависимостей в системе.

🟠Конфигурация (Config)
Конфигурация должна храниться в переменных окружения, а не в коде.
export DATABASE_URL="postgres://user:pass@host:5432/db"


🟠Бэкенд-сервисы (Backing Services)
Внешние сервисы (БД, кэш, API) должны быть заменяемыми и подключаться через URL (без хардкода).

🟠Постоянная сборка, запуск и запуск (Build, Release, Run)
Сборка, релиз и запуск должны быть разделены. Например, Docker-контейнеры для каждой стадии.

🟠Процессы (Processes)
Приложение должно быть бесстатичным (не хранить файлы локально, использовать БД, S3 и т. д.).

🟠Привязка портов (Port Binding)
Приложение должно быть самодостаточным и слушать порт (например, через http.ListenAndServe).

🟠Параллелизм (Concurrency)
Масштабируемость должна обеспечиваться горизонтальным масштабированием (разделением на процессы).

🟠Отказоустойчивость (Disposability)
Приложение должно быстро запускаться и корректно завершаться (например, ловить SIGTERM).

🟠Совместимость Dev/Prod (Dev/Prod Parity)
Среды разработки и продакшена должны быть максимально похожи.

🟠Логирование (Logs)
Логи должны писаться в стандартный вывод и обрабатываться внешними системами (ELK, Loki, Grafana).

🟠Админ-процессы (Admin Processes)
Скрипты администрирования (миграции, отладка) должны выполняться как отдельные процессы.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Go — императивный или декларативный?

Это императивный язык программирования. Код пишется в виде последовательных команд, а не деклараций (как в SQL или Terraform). Однако в некоторых случаях (например, в Kubernetes) Go может использоваться в декларативном стиле.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 В чем разница между буферизированными и небуферизированными каналами?

Каналы — это мощные инструменты для обмена данными между горутинами, обеспечивающие синхронизацию и безопасную коммуникацию. Основное различие между буферизированными и небуферизированными каналами заключается в их поведении при отправке и получении данных.

🚩Небуферизированные каналы

Не имеют внутренней емкости, т.е. они не могут хранить значения. Эти каналы требуют, чтобы отправитель и получатель были готовы обмениваться данными одновременно. Если одна сторона не готова, другая будет заблокирована:
Отправка данных в него блокирует отправителя до тех пор, пока получатель не прочитает данные из канала.
Получение данных из него блокирует получателя до тех пор, пока другая горутина не отправит данные в канал.
ch := make(chan int) // Создание небуферизированного канала
go func() {
val := <-ch // Блокируется, ожидая данные
fmt.Println("Received:", val)
}()
ch <- 3 // Блокируется, пока данные не будут получены


🚩Буферизированные каналы

Имеют внутреннюю емкость, что позволяет хранить одно или несколько элементов без непосредственного получателя данных. Отправка или получение данных работает следующим образом:
Отправка блокируется, только если буфер заполнен. До этого момента данные могут быть отправлены без блокировки, даже если получатель не готов их принять.
Получение из буферизированного канала блокируется, только если канал пуст. Если в канале есть данные, получение происходит без блокировки.
ch := make(chan int, 2) // Создание буферизированного канала с емкостью 2
ch <- 1 // Отправка данных без блокировки
ch <- 2 // Отправка данных без блокировки
go func() {
val := <-ch // Получение данных без блокировки
fmt.Println("Received:", val)
}()


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Главный недостаток стандартного логгера?

Стандартный логгер (log из stdlib):
- Не поддерживает уровни логирования (INFO, ERROR).
- Не форматирует логи по умолчанию.
- Не поддерживает ротацию логов.
Для продакшена обычно используют logrus или zap.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Что такое EXPLAIN?

EXPLAIN — это команда в SQL, которая показывает, как база данных выполняет SQL-запрос. Она помогает оптимизировать запросы, анализируя их план выполнения.

🚩Как работает?

Когда вы пишете EXPLAIN перед SQL-запросом, база данных не выполняет его, а показывает пошаговый процесс выполнения. Это помогает понять:
Какие индексы используются
Какие таблицы сканируются
Какие соединения (JOIN) выполняются
Оценить производительность запроса
EXPLAIN SELECT * FROM users WHERE age > 30;


Выход (пример)
Seq Scan on users  (cost=0.00..25.00 rows=10 width=100)


🚩`EXPLAIN ANALYZE` — глубокий анализ

Добавляет реальное время выполнения запроса
EXPLAIN ANALYZE SELECT * FROM users WHERE age > 30;


🚩Оптимизация запросов с `EXPLAIN`

Использование индексов
CREATE INDEX idx_users_age ON users(age);


Правильный порядок JOIN
EXPLAIN SELECT * FROM orders JOIN users ON orders.user_id = users.id;


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Есть ли для Go хороший ORM?

Да, популярные ORM для Go:
- GORM – самый распространенный, удобный, но может быть медленным.
- Ent – более производительный, но требует генерации кода.
- SQLX – не ORM, но расширяет database/sql и упрощает работу с SQL.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Как преобразовать строку в int и наоборот?

В Go есть несколько способов преобразования строки в число и числа в строку.

🟠Преобразование строки в `int`
Используется пакет strconv и функция strconv.Atoi() или strconv.ParseInt().
package main

import (
"fmt"
"strconv"
)

func main() {
str := "42"
num, err := strconv.Atoi(str) // Преобразуем строку в int
if err != nil {
fmt.Println("Ошибка:", err)
return
}
fmt.Println("Число:", num) // Выведет: Число: 42
}


Пример 2: strconv.ParseInt() (гибкое преобразование)
num, err := strconv.ParseInt("1234", 10, 64) 
// "1234" → строка, 10 → десятичная система, 64 → int64

if err != nil {
fmt.Println("Ошибка:", err)
} else {
fmt.Println("Число:", num) // 1234
}


🟠Преобразование `int` в строку
Используется strconv.Itoa() или strconv.FormatInt().
num := 42
str := strconv.Itoa(num) // 42 → "42"
fmt.Println("Строка:", str) // "42"


Пример 2: strconv.FormatInt() (int64 → строка)
num := int64(12345)
str := strconv.FormatInt(num, 10) // 10 — десятичная система
fmt.Println("Строка:", str) // "12345"


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Стандартный набор метрик Prometheus в Go-программе?

Prometheus в Go-программах собирает такие стандартные метрики:
- Go runtime метрики (go_gc_duration_seconds, go_memstats_heap_alloc_bytes).
- HTTP-метрики (если используется prometheus/client_golang).
- Метрики запросов (http_requests_total, request_duration_seconds).
- Производительность CPU и памяти (process_cpu_seconds_total, process_resident_memory_bytes).
Для их сбора нужно подключить prometheus/client_golang и зарегистрировать метрики в /metrics.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Что такое SSL?

SSL (Secure Sockets Layer) — это протокол, который обеспечивает безопасность передачи данных в интернете, используя шифрование. Он был разработан для защиты данных, передаваемых между клиентом (например, веб-браузером) и сервером (например, веб-сайтом), от перехвата и манипуляций.

🚩Основные функции

🟠Шифрование:
Защита данных от перехвата и чтения посторонними лицами путем их шифрования.
🟠Аутентификация:
Подтверждение подлинности сервера (и иногда клиента) с помощью цифровых сертификатов, что позволяет клиенту убедиться, что он подключен к настоящему серверу.
🟠Целостность данных:
Проверка того, что данные не были изменены во время передачи, с помощью контрольных сумм и хеш-функций.

🚩Как работает

1⃣Установка соединения:
Клиент инициирует соединение с сервером, запрашивая защищенное соединение.
2⃣Передача сертификата:
Сервер отправляет свой цифровой сертификат, который содержит его публичный ключ и информацию о сервере.
3⃣Проверка сертификата:
Клиент проверяет сертификат, используя доверенные центры сертификации (CA), чтобы удостовериться в подлинности сервера.
4⃣Создание сессионных ключей:
Клиент и сервер используют асимметричное шифрование для обмена ключами сеанса, которые затем используются для симметричного шифрования данных в течение сессии.
5⃣Шифрование данных:
Все данные, передаваемые между клиентом и сервером, шифруются с использованием симметричных ключей, обеспечивая безопасность передачи.

🚩Пример использования

При посещении веб-сайта с использованием HTTPS (например, https://example.com), SSL обеспечивает шифрование и безопасность данных, передаваемых между вашим браузером и сервером.

🚩Развитие

🟠SSL 1.0:
Никогда не был выпущен публично из-за серьезных уязвимостей.
🟠SSL 2.0:
Выпущен в 1995 году, но вскоре был признан небезопасным из-за множества уязвимостей.
🟠SSL 3.0:
Выпущен в 1996 году, значительно улучшил безопасность, но со временем также был признан устаревшим из-за уязвимостей (например, POODLE-атака).

🚩TLS как наследник SSL

🟠TLS (Transport Layer Security):
SSL был заменен протоколом TLS, который является его преемником и предлагает улучшенную безопасность. Текущие версии TLS (1.2 и 1.3) используются вместо SSL.
🟠Основные отличия:
TLS обеспечивает более сильное шифрование, лучшее управление сессионными ключами и устранение уязвимостей, найденных в SSL.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Какие преимущества у горутин над тредами?

1. Легковесность – горутины потребляют меньше памяти, чем системные потоки.
2. Планировщик Go – позволяет управлять миллионами горутин без явного управления потоками.
3. Простота работы с конкурентностью – нет необходимости использовать mutex для синхронизации, благодаря каналам.
4. Низкие затраты на переключение контекста – переключение между горутинами быстрее, чем между потоками.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Зачем синхронизировать доступ данных?

В многопоточных (параллельных) программах горутины (goroutines) могут одновременно изменять одни и те же данные. Если не синхронизировать доступ, это приведёт к гонке данных (data race), когда несколько потоков читают/пишут одно и то же значение одновременно.

🚩`sync.Mutex` (мьютексы — блокировка данных)

Используется для блокировки критической секции кода, чтобы в один момент только одна горутина могла изменять данные.
package main

import (
"fmt"
"sync"
)

var (
counter int
mutex sync.Mutex
)

func increment(wg *sync.WaitGroup) {
defer wg.Done()
mutex.Lock() // Блокируем доступ
counter++ // Изменяем данные
mutex.Unlock() // Разблокируем доступ
}

func main() {
var wg sync.WaitGroup

for i := 0; i < 1000; i++ {
wg.Add(1)
go increment(&wg)
}

wg.Wait()
fmt.Println("Итоговый счетчик:", counter) // 1000
}


🚩`sync.RWMutex` (разделяемая блокировка)

Позволяет нескольким горутинам читать данные одновременно, но блокирует запись.
var (
data int
mutex sync.RWMutex
)

func readData(wg *sync.WaitGroup) {
defer wg.Done()
mutex.RLock() // Разрешаем чтение
fmt.Println("Читаем данные:", data)
mutex.RUnlock()
}

func writeData(wg *sync.WaitGroup) {
defer wg.Done()
mutex.Lock() // Блокируем на запись
data++
mutex.Unlock()
}


🚩`sync.WaitGroup` (ожидание горутин)

Позволяет дождаться завершения всех горутин без блокировки данных.
var wg sync.WaitGroup

wg.Add(2) // Ожидаем 2 горутины
go func() {
defer wg.Done()
fmt.Println("Горутина 1 завершилась")
}()
go func() {
defer wg.Done()
fmt.Println("Горутина 2 завершилась")
}()

wg.Wait() // Ждём завершения всех горутин
fmt.Println("Все горутины завершены")


🚩`sync/atomic` (атомарные операции)

Атомарные операции быстрее мьютексов и гарантируют безопасное обновление переменных без гонок данных.
import "sync/atomic"

var counter int64

func incrementAtomic() {
atomic.AddInt64(&counter, 1) // Атомарное увеличение
}


🚩Каналы (лучший способ в Go)

В Go рекомендуется избегать блокировок и использовать каналы для передачи данных между горутинами.
package main

import "fmt"

func main() {
ch := make(chan int) // Канал для передачи данных

go func() {
ch <- 42 // Отправляем данные
}()

data := <-ch // Получаем данные
fmt.Println("Получено:", data)
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM