Golang | Вопросы собесов
4.34K subscribers
29 photos
1 video
710 links
Download Telegram
🤔 В чем отличия InnoDB и MyISAM?

- InnoDB – поддерживает транзакции, внешние ключи, обеспечивает целостность данных, использует кластерные индексы. Подходит для сложных нагрузок.
- MyISAM – быстрее для чтения, но не поддерживает транзакции и блокирует всю таблицу при изменениях. Используется для простых, редко изменяемых данных.
InnoDB предпочтителен для большинства современных приложений, так как надежнее и устойчивее к сбоям.


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

В Go каналы (chan) можно закрывать с помощью close(channel), чтобы показать, что больше не будет отправляться данных.

Как закрыть канал?
ch := make(chan int)
close(ch) // Закрываем канал


Полный пример
package main

import "fmt"

func main() {
ch := make(chan int)

go func() {
for i := 1; i <= 3; i++ {
ch <- i // Отправляем данные
}
close(ch) // Закрываем канал
}()

for val := range ch { // Читаем пока канал не закроется
fmt.Println(val)
}

fmt.Println("Канал закрыт, чтение завершено")
}


Выход
1
2
3
Канал закрыт, чтение завершено


🚩Как проверить, закрыт ли канал?

Используем val, ok := <-ch:
- ok == true → канал открыт, есть данные.
- ok == false → канал закрыт.
package main

import "fmt"

func main() {
ch := make(chan int)
close(ch)

val, ok := <-ch
fmt.Println("val:", val, "ok:", ok) // val: 0 ok: false
}


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

Да, HAVING можно использовать без GROUP BY, но он будет работать как WHERE, применяясь ко всей выборке после агрегации. Например, SELECT COUNT(*) FROM table HAVING COUNT(*) > 10; проверит условие на всю таблицу. Однако, чаще HAVING используется именно с группировкой.


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

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

🚩Базовые типы

🟠Числовые типы
Целые числа:
int (размер зависит от платформы):int8, int16, int32, int64 ; uint (беззнаковый, размер зависит от платформы) ; uint8 (также известен как byte), uint16, uint32, uint64
Числа с плавающей запятой:float32, float64
Комплексные числа:complex64, complex128

🟠Булевый тип
bool (значения true и false)

🟠Символьные типы
byte (аналог uint8)
rune (аналог int32, используется для представления символов Unicode)

🟠Строки
string (последовательность байт, используемая для хранения текста)

🚩Агрегированные типы

🟠Массивы
Фиксированный размер: [n]T (например, [5]int для массива из пяти целых чисел)

🟠Срезы
Переменный размер: []T (например, []int для среза целых чисел)

🟠Структуры
Набор полей: struct (например, type Person struct { Name string; Age int })

🚩Ссылочные типы

🟠Указатели
Хранит адрес переменной: *T (например, *int для указателя на целое число)

🟠Карты (map)
Хранит пары ключ-значение: map[K]V (например, map[string]int для карты, где ключи - строки, а значения - целые числа)

🟠Функции
Определяет тип функции: func(параметры) возвращаемые_типы (например, func(int) int для функции, принимающей целое число и возвращающей целое число)

🟠Интерфейсы
Определяет методы, которые должны быть реализованы: interface{} (например, type Stringer interface { String() string })

package main

import "fmt"

// Определение структуры
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 age int = 30
var name string = "Alice"
var isStudent bool = false

// Пример использования агрегированных типов
var scores [3]int = [3]int{90, 85, 88}
var people []Person = []Person{
{Name: "Bob", Age: 25},
{Name: "Charlie", Age: 35},
}

// Пример использования ссылочных типов
var p *Person = &Person{Name: "Dave", Age: 40}
var ageMap map[string]int = map[string]int{"Alice": 30, "Bob": 25}

// Вывод данных
fmt.Println("Name:", name)
fmt.Println("Age:", age)
fmt.Println("Is student:", isStudent)
fmt.Println("Scores:", scores)
fmt.Println("People:", people)
fmt.Println("Pointer to Person:", p)
fmt.Println("Age map:", ageMap)
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Forwarded from easyoffer
На easyoffer 2.0 появится:
База тестовых заданий

🟠Тестовые задания для разных грейдов
🟠Фильтрация тестовых заданий по технологиям и компаниям

Когда я только начинал учиться на программиста, я постоянно выдумывал себе задачи для практики и тратил на это много времени. Но только в момент поиска работы я столкнулся с тестовыми заданиями, и понял насколько круто они прокачивают навыки. Нужно было еще на этапе обучения пробовать их делать. Все компании стараются составить тестовое задание "под себя", это дает большой выбор в тематике задач и технологий. На easyoffer 2.0 вы сможете отфильтровать тестовые задания по навыкам/грейдам и найти те, что подходят лично вам для практики.

В течение 1-2 дней я объявлю о краудфандинг кампании, чтобы ускорить разработку easyoffer 2.0. Все кто, поддержал проект на этом этапе смогу получить 1 год доступа к сайту по цене месячной подписки и смогут попасть на закрытое бета-тестирование. А первые 150 донатеров получать особо-выгодную цену и бонус.

🚀 Следите за стартом 👉 в этом телеграм канале, в нем информация о старте будет опубликована за 6 часов до официального начала.
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Что такое WaitGroup?

`WaitGroup` в Go используется для синхронизации горутин, чтобы гарантировать, что основная горутина ждёт завершения всех дочерних горутин. Метод `Add()` увеличивает счетчик горутин, метод `Done()` уменьшает его, а метод `Wait()` блокирует выполнение до тех пор, пока счетчик не станет равен нулю. `WaitGroup` удобен для управления потоками выполнения, особенно когда несколько горутин выполняют параллельные задачи. Это упрощает координацию завершения работы всех горутин в программе.

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

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

🚩Основные характеристики

🟠Универсальность
Пустой интерфейс может содержать значение любого типа, потому что все типы в Go автоматически реализуют пустой интерфейс.
🟠Использование
Пустой интерфейс широко используется для создания обобщенных (generic) структур данных, функций и методов, которые могут работать с данными любых типов.

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

🟠Присваивание значений разного типа
Пустой интерфейс может использоваться для хранения значений различных типов в одной переменной.
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 printValue(v interface{}) {
fmt.Println(v)
}

func main() {
printValue(42)
printValue("hello")
printValue(true)
}


🟠Универсальные структуры данных
Пустой интерфейс используется для создания структур данных, которые могут хранить значения различных типов.
package main

import "fmt"

func main() {
var values []interface{}
values = append(values, 42, "hello", true)

for _, v := range values {
fmt.Println(v)
}
}


🟠Обработка разнородных данных
Пустой интерфейс используется для обработки данных различных типов, например, при парсинге JSON.
package main

import (
"encoding/json"
"fmt"
)

func main() {
jsonData := `{"name": "Alice", "age": 30}`
var result map[string]interface{}
json.Unmarshal([]byte(jsonData), &result)

fmt.Println(result)
}


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

🟠Утверждение типа
Утверждение типа позволяет проверить и преобразовать значение пустого интерфейса к конкретному типу.
package main

import "fmt"

func main() {
var i interface{} = "hello"

s, ok := i.(string)
if ok {
fmt.Println("String:", s) // Output: String: hello
} else {
fmt.Println("Not a string")
}

n, ok := i.(int)
if ok {
fmt.Println("Integer:", n)
} else {
fmt.Println("Not an integer")
}
}


🟠Использование `switch` для проверки типа
Конструкция 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
🤔 Что такое Prometheus?

Это инструмент для мониторинга и алертинга. Он собирает метрики, сохраняет их в виде временных рядов и предоставляет мощный язык запросов для анализа данных.


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

В Go обычный map не потокобезопасен. Если несколько горутин одновременно записывают в map, возникнет ошибка "fatal error: concurrent map writes".
Решение: использовать синхронизацию через sync.Mutex, sync.RWMutex или sync.Map.

🚩Использование `sync.Mutex` (мьютекс)

Блокируем доступ на запись и чтение через sync.Mutex.
package main

import (
"fmt"
"sync"
)

type SafeMap struct {
mu sync.Mutex
m map[string]int
}

func (s *SafeMap) Set(key string, value int) {
s.mu.Lock() // Блокируем доступ
defer s.mu.Unlock()
s.m[key] = value
}

func (s *SafeMap) Get(key string) int {
s.mu.Lock() // Блокируем на чтение
defer s.mu.Unlock()
return s.m[key]
}

func main() {
safeMap := SafeMap{m: make(map[string]int)}
var wg sync.WaitGroup

for i := 0; i < 10; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
safeMap.Set(fmt.Sprintf("key%d", i), i)
}(i)
}

wg.Wait()
fmt.Println("Готово:", safeMap.Get("key5")) // Получаем значение без гонок данных
}


🚩Использование `sync.RWMutex` (оптимизация для чтения)

sync.RWMutex позволяет нескольким горутинам читать одновременно, но блокирует запись.
type SafeMap struct {
mu sync.RWMutex
m map[string]int
}

func (s *SafeMap) Set(key string, value int) {
s.mu.Lock() // Блокируем только на запись
defer s.mu.Unlock()
s.m[key] = value
}

func (s *SafeMap) Get(key string) int {
s.mu.RLock() // Разрешаем множественное чтение
defer s.mu.RUnlock()
return s.m[key]
}


🟠Использование `sync.Map` (встроенный потокобезопасный `map`)
sync.Map из стандартной библиотеки уже потокобезопасен, но работает немного медленнее обычного map из-за внутренних механизмов.
package main

import (
"fmt"
"sync"
)

func main() {
var m sync.Map

var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
m.Store(i, i*10) // Потокобезопасная запись
}(i)
}

wg.Wait()

val, ok := m.Load(5) // Потокобезопасное чтение
if ok {
fmt.Println("Значение:", val)
}
}


Использование канала (`chan`) вместо `map`
Вместо map можно передавать данные через канал (chan), если логика позволяет.
package main

import (
"fmt"
)

func main() {
ch := make(chan map[string]int, 1)
ch <- make(map[string]int)

go func() {
m := <-ch
m["key"] = 42
ch <- m
}()

m := <-ch
fmt.Println(m["key"]) // 42
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Forwarded from easyoffer
🎉 Краудфандинг easyoffer 2.0 стартовал!

Друзья, с этого момента вы можете поддержать проект и получить существенный бонус:

🚀 PRO-тариф на 1 год, по цене месячной подписки на релизе.
Доступ к закрытому бета-тесту easyoffer 2.0 (середина–конец мая)

Поддержать проект можно здесь:
https://planeta.ru/campaigns/easyoffer

📌 Если не получается оплатить через карту РФ — напишите мне @kivaiko, и мы найдём удобный способ
Forwarded from easyoffer
Я поставил целью сбора скромные 300 тыс. рублей, но ребята, вы накидали больше млн. всего за 1 день. Это просто невероятно!

Благодаря вашей поддержке, я смогу привлечь еще больше людей для разработки сайта и обработки собеседований. Ваш вклад сделает проект качественнее и ускорит его выход! Огромное вам спасибо!

Краудфандинг будет продолжаться еще 31 день и все кто поддержать проект сейчас, до его выхода, смогут получить:

🚀 PRO-тариф на 1 год, по цене месячной подписки на релизе.
Доступ к закрытому бета-тесту easyoffer 2.0 (середина–конец мая)

Поддержать проект можно здесь:
https://planeta.ru/campaigns/easyoffer

Огромное спасибо за вашу поддержку! 🤝
🤔 Как проводить тестирование в Go?

Тестирование в Go реализуется с помощью встроенного пакета testing. Тесты пишутся как функции с именами, начинающимися на Test, принимающими параметр *testing.T. Для запуска тестов используется команда go test. Также доступны инструменты для бенчмарков (Benchmark), покрытия кода тестами (-cover) и работы с примерами (Example).

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

Массивы и слайсы могут использоваться для хранения последовательности элементов одного типа. Когда вы "подставляете" переменную в массив или слайс, вы просто присваиваете значение этой переменной одному из элементов массива или слайса.

Пример с массивом
package main

import "fmt"

func main() {
// Создаем массив из 5 целых чисел
var arr [5]int

// Создаем переменную и присваиваем ей значение
x := 10

// Подставляем переменную в массив
arr[0] = x

// Выводим массив
fmt.Println(arr) // Выводит: [10 0 0 0 0]
}


🚩Объяснение

1⃣Создание массива
Это создает массив arr из 5 целых чисел, все элементы которого инициализируются значением 0.
var arr [5]int   


2⃣Создание переменной
Создаем переменную x и присваиваем ей значение 10.
x := 10   


3⃣Присваивание значения элементу массива
Присваиваем значение переменной x первому элементу массива arr.
arr[0] = x   


4⃣Вывод массива
Выводит содержимое массива, показывая, что первый элемент массива теперь равен 10, а остальные элементы остаются нулями.
fmt.Println(arr)   


Пример со слайсом
package main

import "fmt"

func main() {
// Создаем слайс из 3 целых чисел
slice := make([]int, 3)

// Создаем переменную и присваиваем ей значение
x := 20

// Подставляем переменную в слайс
slice[1] = x

// Выводим слайс
fmt.Println(slice) // Выводит: [0 20 0]
}


🚩Объяснение

1⃣Создание слайса
Это создает слайс slice из 3 целых чисел, все элементы которого инициализируются значением 0.
slice := make([]int, 3)   


2⃣Создание переменной
Создаем переменную x и присваиваем ей значение 20.
x := 20   


3⃣Присваивание значения элементу слайса
Присваиваем значение переменной x второму элементу слайса slice.
slice[1] = x   


4⃣Вывод слайса
Выводит содержимое слайса, показывая, что второй элемент слайса теперь равен 20, а остальные элементы остаются нулями.
fmt.Println(slice)   


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

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

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

Один из полезных паттернов, который я использовал в реальном проекте на Go — это Worker Pool (пул воркеров).
Этот паттерн помогает ограничить количество горутин при обработке задач, чтобы не перегружать систему.

🚩Задача

В моём проекте был сервис, который обрабатывал тысячи файлов параллельно. Если запускать новую горутину на каждый файл, то память быстро заканчивалась, и сервер "умирал".
Решение: вместо создания тысяч горутин я использовал Worker Pool с фиксированным числом воркеров (например, 5), которые брали задачи из очереди.

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

Есть канал задач (jobs).
Есть пул воркеров (workers), которые читают задачи из канала.
Каждый воркер обрабатывает одну задачу и берёт следующую.
package main

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

// Количество воркеров
const workerCount = 5

// Воркер, который берет задачи из канала и обрабатывает их
func worker(id int, jobs <-chan int, wg *sync.WaitGroup) {
defer wg.Done()
for job := range jobs {
fmt.Printf("Worker %d processing job %d\n", id, job)
time.Sleep(time.Second) // Имитация обработки
}
}

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

// Запускаем воркеров
for i := 1; i <= workerCount; i++ {
wg.Add(1)
go worker(i, jobs, &wg)
}

// Отправляем задачи в канал
for j := 1; j <= 10; j++ {
jobs <- j
}
close(jobs) // Закрываем канал, чтобы воркеры знали, что задачи кончились

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


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

1. Интерфейсы:
- Подходят для обобщения кода, работающего с разными типами, реализующими определенные методы.
2. Дженерики (Go 1.18+):
- Позволяют создавать функции и структуры с параметризованными типами.
3. Композиция функций:
- Использование функций высшего порядка для обработки различных данных.


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

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

🚩Основные компоненты Go `runtime`

Горутины (goroutines) — лёгкие потоки, управляемые Go.
Планировщик (scheduler) — распределяет горутины по потокам ОС.
Модель памяти — управление стеком и кучей.
Сборщик мусора (GC) — автоматически очищает неиспользуемую память.
Syscalls — взаимодействие с ОС.

🚩Горутины и планировщик (scheduler)

Горутины легче, чем потоки ОС: Go может запускать миллионы горутин, а планировщик (M:N) распределяет их по потокам ОС.
Go использует M:N-модель, где:
- M — системные потоки (OS threads).
- N — горутины (goroutines).
- P — процессоры (logical processors) — управляют выполнением горутин.
P привязан к M, а G (горутины) распределяются между `P` динамически.
Это позволяет Go эффективно использовать CPU без ручного управления потоками.
Goroutine (G1, G2, G3, ...) 

Scheduler → P (Processor) → M (OS Thread) → CPU


🚩Управление памятью и стеком

Стек (stack) в Go динамический — он начинается с 2 KB и увеличивается при необходимости.
Если стек заполнен, Go автоматически расширяет его.
В отличие от C, Go не требует malloc/free, так как есть GC (garbage collector).

🚩Сборщик мусора (Garbage Collector)

Go использует автоматический сборщик мусора, который:
Работает параллельно с программой
Минимизирует паузы (low-latency GC)
Очищает неиспользуемые объекты из кучи
import "runtime"

runtime.GC() // Принудительный запуск сборщика мусора (обычно не нужно)


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

Для преобразования используется утверждение типа (type assertion): value, ok := interfaceValue.(TargetType). Оно проверяет, принадлежит ли значение интерфейса целевому типу.


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

Слайсы имеют две основные характеристики: длину (len) и емкость (capacity). Понимание этих характеристик важно для эффективного использования слайсов.

🚩Длина (len)

Это количество элементов, которые в данный момент находятся в слайсе. Она указывает, сколько элементов доступно для чтения или записи.
package main

import "fmt"

func main() {
slice := []int{1, 2, 3, 4, 5}
fmt.Println("Length:", len(slice)) // Length: 5
}


🚩Емкость (capacity)

Это максимальное количество элементов, которые слайс может содержать без выделения дополнительной памяти. Емкость всегда больше или равна длине слайса.
package main

import "fmt"

func main() {
slice := make([]int, 3, 5)
fmt.Println("Length:", len(slice)) // Length: 3
fmt.Println("Capacity:", cap(slice)) // Capacity: 5
}


🚩Взаимосвязь длины и емкости

🟠Длина (`len`)
Определяет текущее количество элементов в слайсе.
Используется для операций чтения и записи.

🟠Емкость (`cap`)
Определяет максимальное количество элементов, которые могут быть добавлены в слайс без выделения новой памяти.
Емкость может увеличиваться автоматически при добавлении элементов через функцию append.

🚩Использование append

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

import "fmt"

func main() {
slice := make([]int, 2, 2)
slice[0] = 1
slice[1] = 2

fmt.Println("Before append:", slice, "Len:", len(slice), "Cap:", cap(slice)) // [1 2] Len: 2 Cap: 2

// Добавляем элемент, превышающий текущую емкость
slice = append(slice, 3)

fmt.Println("After append:", slice, "Len:", len(slice), "Cap:", cap(slice)) // [1 2 3] Len: 3 Cap: 4
}


🚩Полная форма нарезки (full slice expression)

Позволяет задать начальный индекс, конечный индекс и емкость нового слайса.
package main

import "fmt"

func main() {
original := []int{1, 2, 3, 4, 5}
newSlice := original[1:3:4]
fmt.Println("New Slice:", newSlice) // [2 3]
fmt.Println("Length:", len(newSlice)) // 2
fmt.Println("Capacity:", cap(newSlice)) // 3
}


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

rune — это псевдоним для int32, который представляет один символ Unicode. Используется для работы с многоязычными текстами и символами, особенно когда требуется обработка не-ASCII символов. Это полезно в случаях, когда нужно обрабатывать строки на уровне символов, а не байтов.

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

В Go sync.WaitGroup используется для синхронизации выполнения горутин. Она позволяет основной горутине (или любой другой горутине) ждать завершения группы горутин перед продолжением работы. Это особенно полезно, когда нужно убедиться, что все фоновые задачи завершены до выполнения дальнейших действий.

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

🟠Add(delta int)
Увеличивает (или уменьшает) счетчик горутин на заданное значение delta.
Обычно вызывается до запуска горутин, чтобы установить количество горутин, которые нужно дождаться.
🟠Done()
Уменьшает счетчик горутин на 1.
Вызывается горутиной, когда она завершает свою работу.
🟠Wait()
Блокирует выполнение до тех пор, пока счетчик горутин не станет равен нулю.
Обычно вызывается основной горутиной для ожидания завершения всех горутин.

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

Мы используем WaitGroup для ожидания завершения нескольких горутин.
package main

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

func worker(id int, wg *sync.WaitGroup) {
defer wg.Done() // Уменьшает счетчик на 1 при завершении работы горутины
fmt.Printf("Worker %d starting\n", id)
time.Sleep(time.Second)
fmt.Printf("Worker %d done\n", id)
}

func main() {
var wg sync.WaitGroup

for i := 1; i <= 5; i++ {
wg.Add(1) // Увеличивает счетчик горутин на 1
go worker(i, &wg)
}

wg.Wait() // Ожидает завершения всех горутин
fmt.Println("All workers done")
}


🟠Мы создаем 5 горутин, каждая из которых выполняет функцию worker.
🟠Счетчик WaitGroup увеличивается на 1 перед запуском каждой горутины с помощью wg.Add(1).
🟠Каждая горутина вызывает wg.Done() при завершении, уменьшая счетчик на 1.
🟠Основная горутина вызывает wg.Wait(), блокируясь до тех пор, пока все горутины не завершат свою работу.

🚩Почему `WaitGroup` необходимы

🟠Синхронизация выполнения
Позволяет основной горутине дождаться завершения всех запущенных горутин, что особенно важно для корректного завершения программы или выполнения зависимых задач.
🟠Избежание дедлоков
Гарантирует, что основная горутина не завершит выполнение программы до того, как завершатся все горутины, предотвращая возможные дедлоки или незавершенные операции.
🟠Упрощение управления горутинами
Позволяет легко управлять множеством горутин, не требуя сложной логики для отслеживания их завершения.

🚩Пример с ошибкой без `WaitGroup`

Без использования WaitGroup основной поток может завершиться до завершения всех горутин, что приведет к неполной обработке данных. В этом примере использование time.Sleep для ожидания является ненадежным и не гарантирует завершение всех горутин. Вместо этого правильное использование WaitGroup обеспечивает корректное завершение всех задач.
package main

import (
"fmt"
"time"
)

func worker(id int) {
fmt.Printf("Worker %d starting\n", id)
time.Sleep(time.Second)
fmt.Printf("Worker %d done\n", id)
}

func main() {
for i := 1; i <= 5; i++ {
go worker(i)
}

time.Sleep(2 * time.Second) // Это не гарантирует завершение всех горутин
fmt.Println("All workers done")
}


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