Golang | Вопросы собесов
4.34K subscribers
27 photos
1 video
705 links
Download Telegram
🤔 В чем отличия http 1.1 и http 2?

HTTP/2 поддерживает мультиплексирование, позволяя одновременно обрабатывать несколько запросов через одно соединение, тогда как HTTP/1.1 работает по одному запросу за раз. HTTP/2 использует бинарный формат и сжатие заголовков, что ускоряет передачу данных. Эти изменения делают HTTP/2 более производительным и эффективным.

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

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

🚩Основные особенности `sync.Map`

🟠Потокобезопасность
sync.Map изначально спроектирована для безопасного использования в многопоточной среде. Все операции по чтению, записи и удалению синхронизированы.
🟠Простота использования:
sync.Map предоставляет высокоуровневые методы для работы с картой, что упрощает её использование в сравнении с традиционными картами и мьютексами.

🚩Основные методы `sync.Map`

🟠Store(key, value interface{})
Сохраняет значение по заданному ключу. Если ключ уже существует, его значение будет перезаписано.
var m sync.Map
m.Store("foo", 1)


🟠Load(key interface{}) (value interface{}, ok bool):
Возвращает значение, сохраненное по заданному ключу, и булев флаг, указывающий, было ли найдено значение.
value, ok := m.Load("foo")
if ok {
fmt.Println("Found:", value)
} else {
fmt.Println("Not found")
}


🟠LoadOrStore(key, value interface{}) (actual interface{}, loaded bool):
Возвращает существующее значение по ключу, если ключ уже существует, иначе сохраняет и возвращает новое значение. Булев флаг указывает, существовал ли ключ до вызова.
actual, loaded := m.LoadOrStore("foo", 1)
if loaded {
fmt.Println("Existing value:", actual)
} else {
fmt.Println("Stored new value:", actual)
}


🟠Delete(key interface{}):
Удаляет значение по заданному ключу.
m.Delete("foo")


🟠Range(f func(key, value interface{}) bool):
Выполняет функцию для каждого ключа и значения в карте. Если функция возвращает false, обход прекращается.
   m.Store("foo", 1)
m.Store("bar", 2)

m.Range(func(key, value interface{}) bool {
fmt.Println(key, value)
return true // продолжить обход
})


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

Несколько горутин безопасно работают с sync.Map.
package main

import (
"fmt"
"sync"
)

func main() {
var m sync.Map
var wg sync.WaitGroup

// Запись в sync.Map из нескольких горутин
for i := 0; i < 5; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
m.Store(i, i*i)
}(i)
}

wg.Wait()

// Чтение из sync.Map
m.Range(func(key, value interface{}) bool {
fmt.Printf("key: %v, value: %v\n", key, value)
return true
})
}


🚩Когда использовать `sync.Map`

🟠Частое чтение и редкие записи:
sync.Map оптимизирована для сценариев с частыми операциями чтения и редкими операциями записи. Она может быть менее эффективной при частых изменениях структуры карты.
🟠Замена мьютексов и карт:
Если нужно использовать карту в многопоточной среде и не хочется вручную управлять блокировками с помощью мьютексов.
🟠Понятный и простой код:
sync.Map может сделать код более понятным и простым за счет использования высокоуровневых методов вместо ручного управления блокировками.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Разница между WHERE и HAVING?

WHERE фильтрует строки до выполнения группировки данных, а HAVING — после. WHERE используется для условий над исходными данными, а HAVING — для агрегатных функций. Эти операторы работают на разных этапах выполнения SQL-запроса.

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

Объединение карты (map) и мьютексов (mutexes) в Go — это стандартный способ обеспечения потокобезопасности при доступе к данным в многопоточной среде. Это позволяет использовать карту для хранения данных и мьютексы для синхронизации доступа к этим данным.

🚩Основные шаги для объединения карты и мьютексов

🟠Создание структуры с картой и мьютексом
Мы создадим структуру, которая будет содержать карту и мьютекс. Это позволит нам инкапсулировать доступ к карте и обеспечить безопасный доступ через методы этой структуры.
🟠Методы для безопасного доступа:
Реализуем методы для безопасного чтения, записи и удаления элементов карты, используя мьютекс для синхронизации.

🚩Пример реализации

Мы создадим потокобезопасную карту с использованием мьютекса.
package main

import (
"fmt"
"sync"
)

// SafeMap структура, содержащая карту и мьютекс для синхронизации
type SafeMap struct {
mu sync.Mutex
m map[string]int
}

// NewSafeMap создает новый SafeMap
func NewSafeMap() *SafeMap {
return &SafeMap{
m: make(map[string]int),
}
}

// Store сохраняет значение в карту
func (sm *SafeMap) Store(key string, value int) {
sm.mu.Lock()
defer sm.mu.Unlock()
sm.m[key] = value
}

// Load возвращает значение из карты
func (sm *SafeMap) Load(key string) (int, bool) {
sm.mu.Lock()
defer sm.mu.Unlock()
value, ok := sm.m[key]
return value, ok
}

// Delete удаляет значение из карты
func (sm *SafeMap) Delete(key string) {
sm.mu.Lock()
defer sm.mu.Unlock()
delete(sm.m, key)
}

// Range выполняет функцию для каждого ключа и значения в карте
func (sm *SafeMap) Range(f func(key string, value int) bool) {
sm.mu.Lock()
defer sm.mu.Unlock()
for k, v := range sm.m {
if !f(k, v) {
break
}
}
}

func main() {
safeMap := NewSafeMap()

var wg sync.WaitGroup

// Запись в SafeMap из нескольких горутин
for i := 0; i < 5; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
key := fmt.Sprintf("key%d", i)
safeMap.Store(key, i*i)
}(i)
}

wg.Wait()

// Чтение из SafeMap
safeMap.Range(func(key string, value int) bool {
fmt.Printf("key: %s, value: %d\n", key, value)
return true
})
}


1⃣Создание структуры `SafeMap`
Структура SafeMap содержит мьютекс mu и карту m, что позволяет синхронизировать доступ к карте.

2⃣Методы `Store`, `Load`, `Delete`, `Range`
Каждый метод сначала блокирует мьютекс mu для обеспечения эксклюзивного доступа к карте, а затем выполняет соответствующую операцию (сохранение, загрузка, удаление, обход). Метод Range позволяет выполнить функцию для каждого ключа и значения в карте, обеспечивая синхронизацию на время обхода.

🚩Преимущества объединения карты и мьютексов

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

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

Императивный подход описывает, как именно выполнить задачу, задавая пошаговые инструкции. Декларативный подход описывает, что нужно сделать, оставляя детали выполнения системе. Например, в программировании SQL декларативен, а C более императивен

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

RWMutex (Read-Write Mutex) позволяет разделять доступ для чтения и блокировать только для записи, в отличие от обычного Mutex, который блокирует как чтение, так и запись. С помощью RWMutex можно использовать RLock для параллельного доступа к данным в режиме чтения, что увеличивает производительность, если много горутин читают данные и редко их изменяют. Для записи используется Lock, который блокирует доступ к данным для всех других операций чтения и записи.

🚩Обычный мьютекс (sync.Mutex)

sync.Mutex используется для обеспечения эксклюзивного доступа к ресурсу для всех операций (чтение и запись). Подходит для сценариев с частыми операциями записи или равномерным распределением чтений и записей.
var mu sync.Mutex
var counter int

func increment() {
mu.Lock()
counter++
mu.Unlock()
}

func main() {
var wg sync.WaitGroup

for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
increment()
}()
}

wg.Wait()
fmt.Println("Counter:", counter)
}


🚩RW мьютекс (sync.RWMutex)

sync.RWMutex предоставляет два типа блокировок: для чтения (RLock) и для записи (Lock). Позволяет нескольким горутинам одновременно читать данные, если нет активных блокировок для записи. Улучшает производительность в сценариях с частыми чтениями и редкими записями.
var rwMu sync.RWMutex
var counter int

func readCounter() int {
rwMu.RLock()
defer rwMu.RUnlock()
return counter
}

func increment() {
rwMu.Lock()
counter++
rwMu.Unlock()
}

func main() {
var wg sync.WaitGroup

for i := 0; i < 5; i++ {
wg.Add(1)
go func() {
defer wg.Done()
fmt.Println("Read Counter:", readCounter())
}()
}

for i := 0; i < 5; i++ {
wg.Add(1)
go func() {
defer wg.Done()
increment()
}()
}

wg.Wait()
fmt.Println("Final Counter:", counter)
}


🚩Сравнение

🟠Эксклюзивность
sync.Mutex блокирует доступ для всех операций.
sync.RWMutex позволяет одновременное чтение, но блокирует доступ при записи.

🟠Производительность
sync.Mutex подходит для частых записей или равномерного распределения операций.
sync.RWMutex подходит для частых чтений и редких записей.

🟠Применение:
Используйте sync.Mutex для эксклюзивного доступа без различий между чтением и записью.
Используйте sync.RWMutex для улучшения производительности при частых чтениях.

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

Эвакуация — это процесс перемещения объектов в новую область памяти, например из младшего поколения в старшее при сборке мусора. Она происходит, если объект переживает несколько циклов сборки мусора. Этот механизм помогает оптимизировать использование памяти.

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

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

🚩Основные причины голодания

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

🚩Пример голодания

Один поток постоянно захватывает мьютекс, не давая другим потокам шанса выполнить свою работу. В этом примере функция greedyWorker постоянно захватывает мьютекс, блокируя доступ к sharedResource для других горутин (функций starvedWorker). Это приводит к тому, что starvedWorker не получает шанса выполнить свою работу.
package main

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

var mu sync.Mutex
var sharedResource int

func greedyWorker() {
for {
mu.Lock()
sharedResource++
fmt.Println("Greedy worker incremented sharedResource to:", sharedResource)
mu.Unlock()
time.Sleep(100 * time.Millisecond) // Короткая задержка для демонстрации
}
}

func starvedWorker(id int, wg *sync.WaitGroup) {
defer wg.Done()
mu.Lock()
fmt.Printf("Starved worker %d is running\n", id)
sharedResource++
mu.Unlock()
}

func main() {
var wg sync.WaitGroup
wg.Add(5)

go greedyWorker()

for i := 1; i <= 5; i++ {
go starvedWorker(i, &wg)
}

wg.Wait()
fmt.Println("Final sharedResource value:", sharedResource)
}


🚩Способы предотвращения голодания

🟠Справедливые блокировки
Используйте справедливые алгоритмы блокировки, которые предоставляют доступ к ресурсу в порядке очереди.
🟠Управление приоритетами
Обеспечьте сбалансированное управление приоритетами потоков, чтобы потоки с низким приоритетом также получали доступ к ресурсам.
🟠Использование других механизмов синхронизации
Например, вместо использования мьютексов, можно использовать каналы в Go для координации работы между горутинами.
🟠Разделение времени доступа:
Ограничьте время, в течение которого поток может удерживать мьютекс, чтобы другие потоки также могли получить доступ.

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

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

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

Для обеспечения потокобезопасности мапы в Go обычно используется комбинация мьютексов (sync.Mutex) или RW-мьютексов (sync.RWMutex). Давайте рассмотрим, как каждый из них применяется для защиты мапы.

🚩Использование `sync.Mutex`

sync.Mutex обеспечивает эксклюзивный доступ к мапе для всех операций (чтение и запись). Это простой и эффективный способ синхронизации, но при частых операциях чтения может возникнуть узкое место, так как блокируются все операции.
package main

import (
"fmt"
"sync"
)

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

func NewSafeMap() *SafeMap {
return &SafeMap{
m: make(map[string]int),
}
}

func (sm *SafeMap) Store(key string, value int) {
sm.mu.Lock()
defer sm.mu.Unlock()
sm.m[key] = value
}

func (sm *SafeMap) Load(key string) (int, bool) {
sm.mu.Lock()
defer sm.mu.Unlock()
value, ok := sm.m[key]
return value, ok
}

func (sm *SafeMap) Delete(key string) {
sm.mu.Lock()
defer sm.mu.Unlock()
delete(sm.m, key)
}

func main() {
sm := NewSafeMap()
sm.Store("foo", 1)
value, ok := sm.Load("foo")
if ok {
fmt.Println("Found:", value)
} else {
fmt.Println("Not found")
}
}


🚩Использование `sync.RWMutex`

sync.RWMutex предоставляет два типа блокировок: для чтения (RLock) и для записи (Lock). Он позволяет нескольким горутинам одновременно читать данные, если нет активных блокировок для записи. Это улучшает производительность в сценариях с частыми операциями чтения и редкими операциями записи.
package main

import (
"fmt"
"sync"
)

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

func NewSafeMap() *SafeMap {
return &SafeMap{
m: make(map[string]int),
}
}

func (sm *SafeMap) Store(key string, value int) {
sm.mu.Lock()
defer sm.mu.Unlock()
sm.m[key] = value
}

func (sm *SafeMap) Load(key string) (int, bool) {
sm.mu.RLock()
defer sm.mu.RUnlock()
value, ok := sm.m[key]
return value, ok
}

func (sm *SafeMap) Delete(key string) {
sm.mu.Lock()
defer sm.mu.Unlock()
delete(sm.m, key)
}

func main() {
sm := NewSafeMap()
sm.Store("foo", 1)
value, ok := sm.Load("foo")
if ok {
fmt.Println("Found:", value)
} else {
fmt.Println("Not found")
}
}


🚩Когда использовать `sync.Mutex` и `sync.RWMutex`

🟠`sync.Mutex`:
Используйте, если операции чтения и записи происходят с примерно одинаковой частотой. Подходит для простых сценариев, где производительность не критична.
🟠`sync.RWMutex`:
Используйте, если чтения происходят значительно чаще, чем записи. Улучшает производительность за счет одновременного выполнения нескольких операций чтения.

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

Основные структуры данных в Go включают массивы, слайсы, карты (map) и структуры (struct). Они обеспечивают базовую функциональность для работы с данными. Также доступны каналы для синхронизации между потоками.

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

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

🚩Как работает `select` с `default`

Когда select выполняется, он блокируется до тех пор, пока один из его case-блоков не станет готов к выполнению (т.е. канал не станет доступным для отправки или получения данных). Однако, если добавить блок default, select выполнит его немедленно, если ни один из других case-блоков не готов. Это позволяет избежать блокировки горутины и продолжить выполнение других задач.

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

package main

import (
"fmt"
"time"
)

func main() {
ch1 := make(chan int)
ch2 := make(chan int)

go func() {
time.Sleep(2 * time.Second)
ch1 <- 1
}()

go func() {
time.Sleep(1 * time.Second)
ch2 <- 2
}()

for i := 0; i < 5; i++ {
select {
case val := <-ch1:
fmt.Println("Received from ch1:", val)
case val := <-ch2:
fmt.Println("Received from ch2:", val)
default:
fmt.Println("No channel is ready")
time.Sleep(500 * time.Millisecond)
}
}
}


1⃣Мы создали два канала ch1 и ch2.
2⃣Две горутины отправляют значения в эти каналы с задержкой.
3⃣В основном цикле используется select с default, который проверяет готовность каналов каждые 500 миллисекунд.
4⃣Если ни один из каналов не готов, выполняется блок default, выводящий сообщение "No channel is ready".

🚩Когда использовать `default` в `select`

🟠Неблокирующие операции
Если вы хотите проверить наличие данных на канале или возможность отправки данных без блокировки, используйте default.
🟠Избежание дедлоков
В некоторых случаях default может предотвратить дедлоки, позволяя горутине продолжить выполнение, даже если каналы временно недоступны.
🟠Проверка состояния канала
Можно использовать default для периодической проверки состояния канала или выполнения задач в ожидании готовности каналов.

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

SOLID — это набор принципов проектирования: единственная ответственность, открытость/закрытость, подстановки Барбары Лисков, разделение интерфейсов и инверсия зависимостей. Эти принципы помогают создавать гибкие, расширяемые и поддерживаемые системы. Они широко применяются в объектно-ориентированном программировании.

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

Мапы (maps) не являются потокобезопасными по умолчанию. Если несколько горутин одновременно пытаются записывать (или читать и записывать) в одну и ту же мапу, это может привести к непредсказуемым результатам, состояниям гонки и даже краху программы (panic).

🚩Пример проблемы

Вот пример, который иллюстрирует проблему конкурентной записи в мапу. В этом примере множество горутин одновременно записывают значения в мапу m, что приведет к состоянию гонки и, возможно, к панике.
package main

import (
"fmt"
"sync"
)

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

for i := 0; i < 100; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
m[i] = i
}(i)
}

wg.Wait()
fmt.Println("Done")
}


🚩Решение проблемы

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

import (
"fmt"
"sync"
)

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

func NewSafeMap() *SafeMap {
return &SafeMap{
m: make(map[int]int),
}
}

func (sm *SafeMap) Store(key, value int) {
sm.mu.Lock()
defer sm.mu.Unlock()
sm.m[key] = value
}

func (sm *SafeMap) Load(key int) (int, bool) {
sm.mu.Lock()
defer sm.mu.Unlock()
value, ok := sm.m[key]
return value, ok
}

func main() {
sm := NewSafeMap()
var wg sync.WaitGroup

for i := 0; i < 100; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
sm.Store(i, i)
}(i)
}

wg.Wait()

if value, ok := sm.Load(42); ok {
fmt.Println("Found:", value)
} else {
fmt.Println("Not found")
}
}


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

import (
"fmt"
"sync"
)

func main() {
var m sync.Map
var wg sync.WaitGroup

for i := 0; i < 100; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
m.Store(i, i)
}(i)
}

wg.Wait()

if value, ok := m.Load(42); ok {
fmt.Println("Found:", value)
} else {
fmt.Println("Not found")
}
}


🚩Сравнение подходов

🟠sync.Mutex
Позволяет более точный контроль над доступом к мапе. Могут быть использованы для различных структур данных, не только для мап.
🟠sync.Map
Упрощает код, устраняя необходимость в ручной синхронизации. Оптимизирован для сценариев с частыми чтениями и редкими записями.

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

Да, если размер слайса небольшой, линейный перебор может быть быстрее, чем доступ к элементам карты, который включает вычисление хэш-функции. Карты более эффективны для больших данных с частым доступом, но имеют накладные расходы. В таких случаях выбор структуры данных зависит от конкретного сценария.

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

🟠Мьютексы (`sync.Mutex` и `sync.RWMutex`)
sync.Mutex обеспечивает эксклюзивный доступ для чтения и записи.
sync.RWMutex позволяет одновременное чтение несколькими горутинами и эксклюзивный доступ для записи.
var mu sync.Mutex
var rwMu sync.RWMutex

func increment() {
mu.Lock()
counter++
mu.Unlock()
}

func readCounter() int {
rwMu.RLock()
defer rwMu.RUnlock()
return counter
}


🟠Каналы (`chan`)
Синхронизируют горутины через передачу данных. Буферизованные каналы могут хранить несколько значений, небуферизованные требуют синхронной отправки и получения.
ch := make(chan int, 1)
go func() { ch <- 42 }()
fmt.Println(<-ch)


🟠Группы ожидания (`sync.WaitGroup`)
Позволяют основной горутине ждать завершения всех запущенных горутин.
var wg sync.WaitGroup
wg.Add(1)
go func() { defer wg.Done(); /* работа */ }()
wg.Wait()


🟠Потокобезопасные структуры (`sync.Map`)
Потокобезопасная карта для конкурентного доступа.
var m sync.Map
m.Store("key", "value")
value, ok := m.Load("key")


🟠Одноразовая инициализация (`sync.Once`)
Гарантирует выполнение кода только один раз.
var once sync.Once
once.Do(func() { /* инициализация */ })


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

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

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

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

🚩Виды атомарных операций

🟠Чтение и запись
atomic.LoadInt32, atomic.LoadInt64, atomic.LoadUint32, atomic.LoadUint64, atomic.LoadUintptr
atomic.StoreInt32, atomic.StoreInt64, atomic.StoreUint32, atomic.StoreUint64, atomic.StoreUintptr
var x int32
atomic.StoreInt32(&x, 42)
value := atomic.LoadInt32(&x)


🟠Атомарное добавление
atomic.AddInt32, atomic.AddInt64, atomic.AddUint32, atomic.AddUint64
var x int32
atomic.AddInt32(&x, 1)


🟠Атомарное сравнение и замена (compare-and-swap, CAS)
atomic.CompareAndSwapInt32, atomic.CompareAndSwapInt64, atomic.CompareAndSwapUint32, atomic.CompareAndSwapUint64
var x int32 = 42
swapped := atomic.CompareAndSwapInt32(&x, 42, 100)


🟠Атомарное замена
atomic.SwapInt32, atomic.SwapInt64, atomic.SwapUint32, atomic.SwapUint64
var x int32
old := atomic.SwapInt32(&x, 100)


🟠Атомарные операции над указателями
atomic.LoadPointer, atomic.StorePointer, atomic.SwapPointer, atomic.CompareAndSwapPointer
var ptr unsafe.Pointer
atomic.StorePointer(&ptr, unsafe.Pointer(&x))
p := atomic.LoadPointer(&ptr)


🚩Когда использовать

🟠Высокая производительность
Атомарные операции могут быть быстрее, чем использование мьютексов, так как они выполняются без блокировок. Их следует использовать, когда требуется минимальная задержка и высокая производительность.

🟠Простые счетчики и флаги
Атомарные операции идеальны для реализации простых счетчиков, флагов или индикаторов состояния.

🟠Низкоуровневый контроль
Когда требуется низкоуровневый контроль над синхронизацией данных без затрат на мьютексы.

package main

import (
"fmt"
"sync"
"sync/atomic"
)

func main() {
var counter int32
var wg sync.WaitGroup

for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
defer wg.Done()
atomic.AddInt32(&counter, 1)
}()
}

wg.Wait()
fmt.Println("Final Counter:", counter)
}


🚩Ограничения и осторожности

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

🟠Чтение и запись сложных структур
Атомарные операции работают с простыми типами данных (числа, указатели). Для сложных структур данных лучше использовать другие механизмы синхронизации.

🟠Трудночитаемость и поддержка кода
Код, использующий атомарные операции, может быть сложнее для понимания и поддержки, особенно если требуются сложные логические операции.

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

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

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

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

🟠Управление временем выполнения операций
Контексты позволяют задавать дедлайны и таймауты для операций.
context.WithTimeout создает контекст с таймаутом, после которого операция будет автоматически отменена.
ctx, cancel := context.WithTimeout(parentCtx, 2*time.Second)
defer cancel()


context.WithDeadline создает контекст с дедлайном, который определяет точное время, после которого операция будет отменена.
deadline := time.Now().Add(5 * time.Second)
ctx, cancel := context.WithDeadline(parentCtx, deadline)
defer cancel()


🟠Явная отмена операций
Контексты позволяют явно отменять операции, что полезно для управления горутинами и предотвращения утечек памяти.
context.WithCancel создает контекст, который может быть отменен вручную с помощью функции cancel.
ctx, cancel := context.WithCancel(parentCtx)
defer cancel()


🟠Передача метаданных
Контексты позволяют передавать данные между функциями, что полезно для передачи информации о запросах, пользователей и других данных.
context.WithValue создает контекст, который содержит пару ключ-значение для передачи метаданных.
type key string
ctx := context.WithValue(parentCtx, key("userID"), 12345)


🟠Вложенные контексты
Можно создавать иерархии контекстов, где каждый дочерний контекст наследует отмену и дедлайны от родительского.
Создание вложенных контекстов позволяет строить гибкие и сложные системы управления временем выполнения и отмены.
ctx1, cancel1 := context.WithCancel(parentCtx)
ctx2, cancel2 := context.WithTimeout(ctx1, 1*time.Second)
defer cancel1()
defer cancel2()


🟠Синхронизация горутин
Контексты обеспечивают механизм для синхронизации и координации работы нескольких горутин.
Пример синхронизации горутин
var wg sync.WaitGroup
ctx, cancel := context.WithCancel(parentCtx)
defer cancel()

wg.Add(2)
go func() {
defer wg.Done()
worker(ctx, "Worker 1")
}()
go func() {
defer wg.Done()
worker(ctx, "Worker 2")
}()
wg.Wait()

func worker(ctx context.Context, name string) {
for {
select {
case <-ctx.Done():
fmt.Println(name, "stopped")
return
default:
fmt.Println(name, "working")
time.Sleep(1 * time.Second)
}
}
}


🟠Управление жизненным циклом запросов
Контексты широко используются в веб-серверах для управления жизненным циклом HTTP-запросов, обеспечивая таймауты и отмену при завершении обработки запросов.
Пример использования контекста в обработчике HTTP-запроса
func handler(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
select {
case <-time.After(5 * time.Second):
fmt.Fprintln(w, "Request processed")
case <-ctx.Done():
err := ctx.Err()
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}

http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)


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

В Go существуют базовые типы (int, float, string, bool), составные типы (массивы, слайсы, карты, структуры), интерфейсы и каналы. Эти типы обеспечивают разнообразие для работы с различными данными. Также можно создавать пользовательские типы на основе существующих.

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