Golang | Вопросы собесов
4.45K subscribers
30 photos
759 links
Download Telegram
🤔 Как устроены контексты в Go?

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

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

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

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

🟠Сроки выполнения
Контекст может иметь установленный таймаут или дедлайн, после достижения которого он автоматически отменяется.

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

🚩Создание и использование контекста

🟠context.Background()
Возвращает пустой контекст, который никогда не отменяется. Обычно используется в основной функции и при инициализации.

🟠context.TODO()
Используется для указания, что должен быть предоставлен подходящий контекст. Обычно применяется в разработке и при рефакторинге.

🟠context.WithCancel(parent Context) (ctx Context, cancel CancelFunc)
Создает новый контекст с возможностью отмены.

🟠context.WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)
Создает контекст, который автоматически отменяется в указанный deadline.

🟠context.WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
Аналогичен WithDeadline, но устанавливает время жизни контекста на основе заданного таймаута.

func operation1(ctx context.Context) {
select {
case <-time.After(5 * time.Second):
fmt.Println("operation1 completed")
case <-ctx.Done():
fmt.Println("operation1 cancelled")
}
}

func main() {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()

go operation1(ctx)

// Дожидаемся завершения или отмены операции
<-ctx.Done()
if err := ctx.Err(); err != nil {
fmt.Println("main:", err)
}
}


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

1. len: текущая длина слайса, то есть количество элементов, доступных для использования.
2. capacity: общая ёмкость слайса, включая длину и свободное пространство, доступное для добавления новых элементов


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

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

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

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

🚩Пример простого приложения

Простого веб-сервера:
package main

import (
"fmt"
"net/http"
)

func helloHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, World!")
}

func main() {
http.HandleFunc("/", helloHandler)
fmt.Println("Starting server on :8080")
http.ListenAndServe(":8080", nil)
}


🚩Основные концепции

🟠Горутины
Легковесные потоки для параллельных вычислений.
go func() {
fmt.Println("Hello from a goroutine!")
}()


🟠Каналы
Средства обмена данными между горутинами.
ch := make(chan int)
go func() {
ch <- 42
}()
value := <-ch
fmt.Println(value)


🟠Интерфейсы
Абстракция поведения без привязки к реализации.
type Stringer interface {
String() string
}


🟠Пакеты
Организация кода для управления и повторного использования.
package mypackage

import "fmt"

func MyFunction() {
fmt.Println("Hello from mypackage")
}


🚩Применение Go

Go используется для серверного ПО, облачных сервисов, микросервисов, инструментов командной строки и систем с высокой производительностью. Известные проекты на Go включают Kubernetes, Docker, Prometheus, а также Google, Dropbox и Netflix.

🚩Плюсы и минусы

Высокая производительность
Быстро компилируемый язык.
Простота и читаемость
Минимум синтаксического шума.
Конкурентность
Простые механизмы параллельных вычислений.
Богатая библиотека
Широкий набор встроенных инструментов.
Ограниченные обобщения
Ранее отсутствовали, добавлены в Go 1.18.
Нет исключений
Используется управление ошибками через возвращаемые значения.

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

Контекст (context) в Go — это механизм для передачи метаданных и управления временем выполнения горутин, который часто используется для управления тайм-аутами, дедлайнами и отменой операций. Контекст передаётся через аргументы функций и может содержать ключи для хранения значений, а также методы для отмены операций (`WithCancel()`, `WithTimeout()`, `WithDeadline()`). Контексты помогают управлять зависимыми горутинами, гарантируя их завершение при отмене родительской операции. Это важно для эффективного использования ресурсов в многозадачных программах.

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

В Go, проверка типа интерфейса может быть выполнена несколькими способами: с помощью утверждения типа (type assertion) и с помощью конструкции switch для выбора типа.

🚩Утверждение типа (Type Assertion)

Утверждение типа позволяет проверить, является ли значение определенного интерфейса конкретным типом. Если да, то оно преобразует интерфейс в этот тип.
value, ok := interfaceValue.(ConcreteType)


🟠value
Значение типа ConcreteType, если утверждение типа успешно.
🟠ok
Булевое значение, указывающее, удалось ли преобразование.

package main

import "fmt"

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

// Утверждение типа
s, ok := i.(string)
if ok {
fmt.Println("String:", s)
} else {
fmt.Println("Not a string")
}

// Утверждение типа, которое вызовет панику, если тип не соответствует
// Uncomment the line below to see the panic
// s := i.(string)
// fmt.Println(s)
}


🚩Использование `switch` для выбора типа

Конструкция switch позволяет проверить значение интерфейса на соответствие нескольким возможным типам.
switch v := interfaceValue.(type) {
case ConcreteType1:
// v имеет тип ConcreteType1
case ConcreteType2:
// v имеет тип ConcreteType2
default:
// v имеет другой тип
}


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")
printType(42)
printType(true)
printType(3.14)
}


Пример использования для проверки и работы с интерфейсами
package main

import (
"fmt"
)

type Person struct {
Name string
Age int
}

// Реализация интерфейса fmt.Stringer для типа Person
func (p Person) String() string {
return fmt.Sprintf("%s (%d years old)", p.Name, p.Age)
}

func printValue(i interface{}) {
if str, ok := i.(fmt.Stringer); ok {
fmt.Println("Stringer:", str.String())
} else {
fmt.Println("Not a Stringer")
}
}

func main() {
p := Person{Name: "Alice", Age: 30}
printValue(p) // Проверка типа fmt.Stringer
printValue("Hello, world!") // Строка не реализует fmt.Stringer
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Forwarded from easyoffer
🎉 easyoffer 2.0 — релиз уже в этом месяце!

Вас ждут новые фичи, о которых мы ранее даже не упоминали. Они сделают путь к офферам ещё быстрее и эффективнее. Расскажу о них чуть позже 👀

В честь запуска мы готовим ограниченную акцию:

Первые 500 покупателей получат:
🚀 PRO тариф на 1 год с 50% скидкой

Что нужно сделать:

🔔 Подпишитесь на этот Telegram-канал, чтобы первыми узнать о старте релиза. Сообщение появится в нем раньше, чем где-либо еще — вы успеете попасть в число первых 500 и получить максимальную выгоду. 🎁 А еще только для подписчиков канала ценный бонус в подарок к PRO тарифу.

📅 Официальный запуск — уже совсем скоро.
Следите за новостями и не пропустите старт!
🤔 По какому алгоритму растёт slice?

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


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

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

🚩Императивное программирование

Это стиль программирования, где выражается последовательность команд для выполнения задач. В этом подходе программист указывает машине, как изменять своё состояние пошагово, контролируя поток выполнения через управляющие конструкции, такие как циклы, условные операторы и т.д.
🟠Подробное описание процесса
Программист должен указывать все шаги, которые необходимо выполнить для достижения результата.
🟠Примеры языков
Java, C, Python в их традиционном использовании.
🟠Управление состоянием
Императивные программы часто включают явное управление состоянием и его изменениями.
# Императивный подход к сортировке массива методом пузырька
def bubble_sort(array):
n = len(array)
for i in range(n):
for j in range(0, n-i-1):
if array[j] > array[j+1]:
array[j], array[j+1] = array[j+1], array[j]
return array


🚩Декларативное программирование

Это стиль программирования, где описывается желаемый результат, но не детализируется процесс его достижения. В декларативном стиле программа определяет, что должно быть сделано, а не как.
🟠Абстракция от процесса
Описываются желаемые свойства результата, а система сама определяет, как его достичь.
🟠Примеры языков
SQL, HTML, CSS, функциональные языки программирования, такие как Haskell.
🟠Сокрытие сложности
Декларативный подход часто предполагает высокий уровень абстракции, что уменьшает количество деталей, которые нужно учитывать.

-- Декларативный запрос в SQL для получения списка сотрудников, отсортированного по зарплате
SELECT name, salary FROM employees ORDER BY salary DESC;


🚩Основные различия

🟠Описание vs. результат
Императивное программирование фокусируется на описании шагов, необходимых для достижения результата, в то время как декларативное программирование описывает желаемый результат без спецификации конкретных шагов.

🟠Управление состоянием
Императивный подход требует активного управления состоянием программы, в то время как в декларативном подходе состояние управляется системой или вовсе абстрагировано.

🟠Поддержка языка
Разные языки поддерживают разные стили программирования. Некоторые языки, как JavaScript, могут поддерживать оба стиля в зависимости от использования.

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

Зависит от уровня, но в общем:
- На уровне базы: индексация, query plan, нормализация, кэширование.
- На уровне кода: профилирование, снижение сложности алгоритмов.
- На уровне сети: сжатие, уменьшение количества запросов.
- На уровне архитектуры: кэш, очереди, шардирование, микросервисы, отказ от лишнего состояния.
- На уровне ОС/серверов: тюнинг параметров, балансировка, масштабирование.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4👍2
🤔 Что такое 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
👍1
🤔 Что такое iota?

iota — это встроенный идентификатор для автоматической генерации последовательностей чисел, обычно используемый в декларациях констант. Его значение начинается с 0 и увеличивается на 1 для каждой последующей константы в блоке.

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

Обладает несколькими ключевыми отличиями от Java и Python, что делает его уникальным и подходящим для определенных задач.

🟠Компиляция и выполнение
Go: Компилируемый язык, компилируется в машинный код, что обеспечивает высокую производительность и быстрое время выполнения.
Java: Компилируется в байт-код, который выполняется на виртуальной машине Java (JVM). Это обеспечивает переносимость, но может добавлять накладные расходы.
Python: Интерпретируемый язык, что делает его менее производительным по сравнению с компилируемыми языками.

🟠Простота и лаконичность
Go: Разработан для простоты и читаемости, минимизирует синтаксическую сложность, избегает избыточности.
Java: Сложный и многословный синтаксис, требует больше кода для выполнения тех же задач.
Python: Простой и читаемый синтаксис, который делает его легким для изучения и использования.

🟠Управление памятью
Go: Автоматическая сборка мусора, но с управляемыми задержками для обеспечения высокой производительности.
Java: Автоматическая сборка мусора на JVM, что может приводить к задержкам в критических приложениях.
Python: Автоматическая сборка мусора с использованием подсчета ссылок и циклического сборщика мусора.

🟠Конкурентность и параллелизм
Go: Встроенная поддержка конкурентности через горутины и каналы, легковесные потоки с низкими накладными расходами.
Java: Многопоточность с использованием потоков, сложное управление потоками и синхронизацией.
Python: Поддержка многопоточности, но ограниченная глобальной блокировкой интерпретатора (GIL), что затрудняет использование многопоточности для параллельных вычислений.

🟠Статическая и динамическая типизация
Go: Статически типизированный язык, ошибки типа обнаруживаются на этапе компиляции, что повышает надежность кода.
Java: Статически типизированный язык, что позволяет обнаруживать ошибки типа на этапе компиляции.
Python: Динамически типизированный язык, типы проверяются во время выполнения, что может приводить к ошибкам времени выполнения.

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

🟠Стандартная библиотека
Go: Богатая стандартная библиотека с встроенной поддержкой работы с сетью, веб-серверами и другими задачами.
Java: Широкая стандартная библиотека с обширной поддержкой различных API и утилит.
Python: Обширная стандартная библиотека, особенно сильная в области научных вычислений, обработки данных и веб-разработки.

🟠Экосистема и инструменты
Go: Современные и мощные инструменты для сборки, тестирования и профилирования. Простая система управления зависимостями.
Java: Зрелая экосистема с множеством фреймворков и инструментов (например, Maven, Gradle, Spring).
Python: Обширная экосистема пакетов и библиотек (например, pip, virtualenv, Django).

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

GMP — это модель планировщика горутин в Go, состоящая из трёх компонентов:
- G (goroutine) — сама горутина, единица выполняемого кода.
- M (machine) — системный поток ОС, на котором выполняется G.
- P (processor) — логический процессор, владеющий очередью задач и управляющий выполнением G.
Каждому P соответствует одна очередь горутин, и количество P определяет количество одновременно выполняемых горутин (ограничивается через GOMAXPROCS).
Когда M свободен и есть активный P, он берёт G из очереди и выполняет. Это позволяет планировщику балансировать и масштабировать задачи без участия ОС.


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

Планировщик задач (Scheduler) управляет выполнением процессов и горутин (в случае Go). В контексте операционных систем и языков программирования, таких как Golang, планировщик отвечает за распределение вычислительных ресурсов.

🟠G (Goroutine) – Горутинa
Это легковесный поток, который выполняет код. В отличие от ОС-потоков, она не требует большого количества памяти и управляется рантаймом Go.
Включает стек, контекст выполнения, а также статус (ждёт, выполняется, завершена).
Переключается между потоками ОС прозрачно для разработчика.
Можно создать десятки тысяч горутин без значительных затрат памяти.
go func() {
fmt.Println("Привет из горутины!")
}()


🟠M (Machine) – Операционный поток
M (Machine) представляет собой реальный поток ОС (kernel thread), на котором выполняются горутины.
Соответствует потокам ОС (pthread в Linux, Windows Thread в Windows).
Может одновременно выполнять только одну горутину.
Количество M зависит от GOMAXPROCS, но обычно Go создаёт столько потоков, сколько ядер в системе.
runtime.GOMAXPROCS(4) // Использовать 4 ядра процессора


🟠P (Processor) – Планировщик (Scheduler)
Это сущность, которая отвечает за распределение горутин (G) между потоками (M).
P управляет очередью горутин и раздаёт их потокам (M).
По умолчанию количество P равно количеству GOMAXPROCS.
Горутину нельзя выполнить без доступного P.
[Processor P1] ----> [Machine M1] ----> Выполняет горутины G1, G2
[Processor P2] ----> [Machine M2] ----> Выполняет горутины G3, G4


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

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


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

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

🚩Что произойдет при сложении строк?

🟠Создание новой строки
Когда вы складываете строки, Go создает новую строку, содержащую все символы исходных строк, соединенных друг за другом. Исходные строки при этом остаются неизменными.
s1 := "Hello"
s2 := "World"
result := s1 + " " + s2
fmt.Println(result) // Output: Hello World


🟠Новая строка размещается в памяти
Так как строки в Go неизменяемы, результирующая строка создается заново, а память для нее выделяется в куче или на стеке, в зависимости от размера и контекста.

🚩Особенности сложения строк в Go

🟠Эффективность
Сложение строк через оператор + может быть неэффективным при множественных операциях, так как каждый раз создается новая строка, а старые промежуточные результаты удаляются сборщиком мусора.
result := ""
for i := 0; i < 5; i++ {
result += "Go"
}
fmt.Println(result) // Output: GoGoGoGoGo


🟠Использование `strings.Builder` для оптимизации
Если требуется объединить много строк, рекомендуется использовать strings.Builder. Это более эффективный способ, так как Builder работает с буфером и избегает лишних аллокаций.

   import "strings"

func main() {
var builder strings.Builder
for i := 0; i < 5; i++ {
builder.WriteString("Go")
}
fmt.Println(builder.String()) // Output: GoGoGoGoGo
}


🚩Особенности Unicode

Go строки поддерживают Unicode, поэтому сложение строк корректно работает с многобайтовыми символами.
s1 := "Привет"
s2 := "Мир"
result := s1 + " " + s2
fmt.Println(result) // Output: Привет Мир


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

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

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

Паттерн "Очередь" (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
👍2
🤔 Что такое тип byte?

byte — это псевдоним для uint8, представляющий один байт данных. Используется для работы с необработанными данными, такими как файлы или сетевые пакеты, а также для операций с массивами и срезами байтов.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
🤔 По какому алгоритму растет 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
👍3