Golang вопросы собеседований
13.5K subscribers
625 photos
3 videos
1 file
432 links
@notxxx1 - админ

@Golang_google - Golang для разработчиков

@itchannels_telegram - 🔥лучшие из ит

@golangl - chat

@golangtests - golang tests

@golang_jobsgo - go chat jobs

@ai_machinelearning_big_data - AI

@data_analysis_ml

РКН: clck.ru/3FmtKd
加入频道
🔥 Полный курс Golang разработчика 2023 года. Часть1.

1. О курсе - https://www.youtube.com/watch?v=Nv0vmR5OqRg&t=34s

2. Установка GO - https://www.youtube.com/watch?v=gi6gAhzUhUg

3. Пакеты в Golang https://www.youtube.com/watch?v=RNVhf-zBpdQ

4. Типы данных в Go - https://www.youtube.com/watch?v=_ilP500FEP8&t=16s

5. Строки и руны - https://www.youtube.com/watch?v=wUQB74nNxos&t=27s

6. Инструменты CLI- https://www.youtube.com/watch?v=LzqVFx6yOAs&t=8s

7. Введение в переменные - https://www.youtube.com/watch?v=zQa85CrYQJ4&t=190s

8. Переменные на практике - https://www.youtube.com/watch?v=P9Lk0EiO2pU&t=1s

9. Упражнения с переменными -

https://www.youtube.com/watch?v=huj6RHQC34I&t=1s

10. Работа с функциями - https://www.youtube.com/watch?v=euudha7zAiU&t=7s

11. Функции на практике - https://www.youtube.com/watch?v=YdoMOD6H_nk

12. Управление потоками с операторами if else - https://www.youtube.com/watch?v=Mq6WQy_eq_k&t=1s

13. Оператор Switch
- https://www.youtube.com/watch?v=4REsob985vc

14. Циклы - https://www.youtube.com/watch?v=gpt8IGahbXo

15. Структуры - https://www.youtube.com/watch?v=VrOflBI4f_E&t=311s

16. Массивы - https://www.youtube.com/watch?v=QrVHFGtCAnE

17. Срезы - https://www.youtube.com/watch?v=XA1WFY1kv2o&t=671s

18. Карты - https://www.youtube.com/watch?v=XA1WFY1kv2o&list=PLysMDSbb9HcxpAb8lhnMaRpX890wSLz66&index=18

19.Указатели - https://www.youtube.com/watch?v=LYFOJhP28Ro

20. Методы - https://www.youtube.com/watch?v=hmWwp04_XC0

21. IOTA - https://www.youtube.com/watch?v=La67BqQdoys

22.FMT - https://www.youtube.com/watch?v=-Ie4-R23VTk

23.INIT - https://www.youtube.com/watch?v=riIi-FpSOuc

24 Тесты- https://www.youtube.com/watch?v=AprUuq_rm0I

25 Интерфейсы - https://www.youtube.com/watch?v=Amun1J6KE3Q&t=70s

26. Обработка ошибок - https://www.youtube.com/watch?v=VTmHbN0Y4aU

Полный плейлист

📌Курс на stepik

Полный плейлист - https://www.youtube.com/watch?v=XyVihrOSN80&list=PLysMDSbb9HcxpAb8lhnMaRpX890wSLz66&index=19

📌Курс на stepik

@golang_interview
👣 Описание наиболее важных обновление последней версии Go.

go install golang.org/dl/go1.21rc2@latest
~/go/bin/go1.21rc2 download
export GOROOT=/Users/jason/sdk/go1.21rc2/

https://www.dolthub.com/blog/2023-07-07-golang-1.21-release/


@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
🖥 Как масштабировать контейнер?

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

Пример: Системные администраторы могут масштабировать контейнеры Docker, изменяя количество контейнеров или количество ресурсов, выделяемых каждому контейнеру. Чтобы изменить количество контейнеров, можно использовать команду 'docker-compose up' с флагом '--scale'.

Чтобы настроить количество ресурсов, можно использовать команду docker run с флагами '--memory' и '--cpu-shares'. Ограничением, которое может возникнуть в процессе масштабирования, является память. Для успешного масштабирования пакета Docker контейнеру может потребоваться достаточно памяти, чтобы справиться с возросшей вычислительной мощностью.

@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
👣 Какая сложность у следующего
алгоритма?

func reverse(n []int) {
length := len(n)

for i := 0; i < length / 2; i++ {
other := length - i - 1
temp := n[i]

n[i] = n[other]
n[other] = temp
}
}

func reverse(n []int) {
for n / 2 раз
}


Думаете O(log n)? Казалось бы да, но нет.

Ответ: O(n/2), т.к. мы просто один раз берем половину элементов массива и итерируем их, скажем было 100 элементов взяли половину - 50, для Big O это все те же n элементов, следовательно сложность алгоритма O(n)


@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
Что такое fuzzing-тестирование? Как устроен Fuzzing в Go?

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

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

В стандартном фреймворке для тестирования есть специальные типы тестов — Test и Benchmark.

В первой конструкции (картинка) мы объявляем фреймворку, в каких случаях тест можно считать успешным, а в каких — провальным. Во второй — фреймворк пытается подобрать такое количество итераций, на котором можно достаточно точно измерить среднее время выполнения одной итерации. По сути, Fuzzing объединяет Test и Benchmark.

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

Вот ключевые моменты, которые важно запомнить для подготовки fuzzing-тестов:

- Название метода нужно записывать через приставку Fuzz — например, FuzzTest, FuzzBug или FuzzFoo. Иначе Go инициализирует обычный unit-тест.

- В качестве параметра необходимо передать указатель на testing.F. Этот параметр нужен, чтобы «связать» тест с кодом программы.

@golang_interview
Вакансия: Golang Developer  Компания: Lamoda Tech
Формат: удаленный или гибридный
Занятость: полная занятость

В Lamoda Tech мы делаем моду ближе для миллионов пользователей. Наша команда совершает цифровую революцию в fashion и e-commerce.

Приглашаем в нашу команду Golang разработчиков!


Основные требования

- опыт коммерческой разработки бэкенда на Golang от 2-х лет;
- опыт работы с SQL базами данных
- умение проектировать API сервисов;
- опыт работы с нереляционными базами данных
- опыт работы с Docker
- опыт работы с Git

Мы предлагаем

- работу в гибридном режиме — можно приезжать в офис или работать из дома
- бесплатные сессии с коучами и психологами
- оплату участия в профессиональных конференциях
- ноутбук и другая необходимая техника для работы
- ДМС с первого месяца, со стоматологией и чек-апом здоровья раз в год;
- Ежемесячные промокоды на Lamoda до 25%;
- офис на Полежаевской с йогой, тренажерным залом и капсулой сна, а также коворкинг в центре Санкт-Петербурга.

Узнать больше
👣 Modern Go Application

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

Github

@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
👣 Тестовое задание на разработки балансировщика нагрузки на Go

В данном случае используется довольно простой алгоритм Round-Robin.

Начнем с базового объекта server, который содержит 3 атрибута, связанных с сервером

Address()
IsAlive()
Функция Serve() для работы с запросами

type Server interface {
Address() string
IsAlive() bool
Serve(rw http.ResponseWriter, req *http.Request)
}


В дальнейшем используется второй тип объекта – simpleServer.

type simpleServer struct {
addr string
proxy *httputil.ReverseProxy
}


После определения методов, используемых в указанных объектах, мы создаем loadbalancer вместе с функцией getNextAvailableServer().
func NewLoadBalancer(port string, servers []Server) *LoadBalancer {
return &LoadBalancer{
port: port,
roundRobinCount: 0,
servers: servers,
}
}


func (lb *LoadBalancer) getNextAvailableServer() Server {
server := lb.servers[lb.roundRobinCount%len(lb.servers)]
for !server.IsAlive() {
lb.roundRobinCount++
server = lb.servers[lb.roundRobinCount%len(lb.servers)]
}
lb.roundRobinCount++
return server
}


Вызываем main()
func main() {
servers := []Server{
newSimpleServer("https://snapcraft.io"),
newSimpleServer("https://github.com/sambhavsaxena"),
newSimpleServer("http://localhost:3001"),
}
lb := NewLoadBalancer("3000", servers)
handleRedirect := func(rw http.ResponseWriter, req *http.Request) {
lb.serveProxy(rw, req)
}
http.HandleFunc("/", handleRedirect)
fmt.Printf("distributing requests fired at 'localhost:%s'\n", lb.port)
http.ListenAndServe(":"+lb.port, nil)
}


Статья
Полный код

@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
Изменение порядка следования элементов связного списка

Сложность: Средняя

Условие задачи : дан связный список:
L0 → L1 → … → Ln - 1 → Ln.

Надо переопределить порядок следования элементов на следующий:
L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → …

Пример:

Ввод: head = [1,2,3,4]
Вывод: [1,4,2,3]

Ввод: [1,2,3,4,5]
Вывод: [1,5,2,4,3]

Решение задачи

Пишите свое решение в комментариях👇

@golang_interview
⚡️Маст-хэв список для программистов, каналы с последними книжными новинками, библиотеками, разбором кода и актуальной информацией, связанной с вашим языком программирования.
Лучший способ получать свежие обновлении и следить за трендами в разработке.

Python: t.me/pythonl
C#: t.me/csharp_ci
C/C++/ t.me/cpluspluc
Машинное обучение: t.me/ai_machinelearning_big_data
Data Science: t.me/data_analysis_ml
Devops: t.me/devOPSitsec
Go: t.me/Golang_google
Базы данных: t.me/sqlhub
Rust: t.me/rust_code
Javascript: t.me/javascriptv
React: t.me/react_tg
PHP: t.me/phpshka
Android: t.me/android_its
Мобильная разработка: t.me/mobdevelop
Linux: t.me/+A8jY79rcyKJlYWY6
Big Data: t.me/bigdatai
Хакинг: t.me/linuxkalii
Java: t.me/javatg

💼 Папка с вакансиями: t.me/addlist/_zyy_jQ_QUsyM2Vi
Папка Go разработчика: t.me/addlist/MUtJEeJSxeY2YTFi
Папка Python разработчика: t.me/addlist/eEPya-HF6mkxMGIy

📕 Бесплатные Книги для программистов: https://yangx.top/addlist/YZ0EI8Ya4OJjYzEy

🎞 YouTube канал: https://www.youtube.com/@uproger

😆ИТ-Мемы: t.me/memes_prog

🇬🇧Английский: t.me/english_forprogrammers
Please open Telegram to view this post
VIEW IN TELEGRAM
👣 Расскажите про шаблон проектирования Стратегия? Приведите пример реализации на Go.

Стратегия (. Strategy) — поведенческий шаблон проектирования, предназначенный для определения семейства алгоритмов, инкапсуляции каждого из них и обеспечения их взаимозаменяемости. Это позволяет выбирать алгоритм путём определения соответствующего класса. Шаблон Стратегия (. Strategy) позволяет менять выбранный алгоритм независимо от объектов-клиентов, которые его используют.

Реализация шаблона представлена на кратинке.

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

Поведение для объекта toy определено в интерфейсе, а фактическая реализация поведения, например, Recite, выполняется отдельно различными конкретными типами, например, SpiderMan, SuperMan и BatMan.

Если бы поведение toy было закодировано в конкретном типе, то мы были бы заблокированы на использование только этого конкретного поведения.

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

Стратегия позволяет алгоритму изменяться независимо от
клиентов, которые его используют".

Пишите свой пример реализации шаблона в комментариях👇

@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
👣 Experimenting with project templates

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

Gonew - экспериментальный инструмент для создания новых проектов на языке Go на основе предопределенных шаблонов. Любой программист может написать шаблоны, которые упаковываются и распространяются как модули.

Начните с установки gonew с помощью go install:

$ go install golang.org/x/tools/cmd/gonew@latest

Чтобы скопировать существующий шаблон, запустите gonew в родительском каталоге нового проекта с двумя аргументами: первый - путь к шаблону, который необходимо скопировать, второй - имя модуля создаваемого проекта. Например:

$ gonew golang.org/x/example/helloserver example.com/myserver
$ cd ./myserver


А затем вы можете читать и редактировать файлы в ./myserver для настройки.

Для начала мы написали два шаблона:

hello: Инструмент командной строки, печатающий приветствие, с флагами настройки.
helloserver: HTTP-сервер, передающий приветствия.

https://go.dev/blog/gonew

Еще примеры: https://github.com/GoogleCloudPlatform/go-templates

@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
👣 Расскажите про внутреннее устройство mutex

Мьютекс гораздо более комплексная структура по сравнению с atomic, чем может показаться на первый взгляд. Прежде всего у нас есть два вида мютексов: sync.Mutex и sync.RWMutex. У каждого из них есть методы Lock и Unlock, у RWMutex есть дополнительно метод RLock (блокирование для чтения). В обоих типах методы Lock довольно долгие по сравнению с атомиками.

У Mutex код метода Lock включает два вида блокировки. Первый вариант - это когда удается захватить не заблокированный мьютекс, второй вариант довольно долгий и он запускается если мьютекс заблокирован:

// Lock locks m.
// If the lock is already in use, the calling goroutine
// blocks until the mutex is available.
func (m *Mutex) Lock() {
// Fast path: grab unlocked mutex.
if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
if race.Enabled {
race.Acquire(unsafe.Pointer(m))
}
return
}
// Slow path (outlined so that the fast path can be inlined)
m.lockSlow()
}

Первый вариант довольно быстрый и включает в себя одну atomic операцию. Второй вариант включает довольно много кода и здесь подробно разбираться не будет: там также используются атомики в процессе блокировки, но очевидно, что он исполняется еще дольше первого варианта (Fast path).

У RWMutex код метода Lock включает в себя вызов метода Lock структуры Mutex:

// Lock locks rw for writing.
// If the lock is already locked for reading or writing,
// Lock blocks until the lock is available.
func (rw *RWMutex) Lock() {
if race.Enabled {
_ = rw.w.state
race.Disable()
}
// First, resolve competition with other writers.
rw.w.Lock()
// Здесь пропущена часть кода ...
}
}

Да и сама структура RWMutex включает в себя структуру Mutex как одно из полей:

type RWMutex struct {
w Mutex // held if there are pending writers
// Здесь пропущена часть кода ...
}


Метод RLock у RWMutex гораздо быстрее и содержит меньше кода, но тем не менее не быстрее атомика, который там задействован:

func (rw *RWMutex) RLock() {
// Здесь пропущена часть кода ...
if atomic.AddInt32(&rw.readerCount, 1) < 0 {
// A writer is pending, wait for it.
runtime_SemacquireMutex(&rw.readerSem, false, 0)
}
//


📌Вопрос что производительнее атомики или мьютексы?

Напишите свой ответ в комментариях👇

@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
👣 Для чего нужен тип http.ResponseController?

http.ResponseController:
Позволяет переопределять ваши общесерверные таймауты/дедлайны чтения и записи новыми для каждого отдельного запроса.

Шаблон использования интерфейсов http.Flusher и http.Hijacker стал более понятным и менее сложным. Нам больше не нужны никакие утверждения типов!

Он делает проще и безопаснее создание и использование пользовательских реализаций http.ResponseWriter.

Таймауты для отдельных запросов
http.Server Go имеет настройки ReadTimeout и WriteTimeout, которые вы можете использовать для автоматического закрытия HTTP-соединения, если время, затраченное на чтение запроса или запись ответа, превышает какое-либо фиксированное значение. Эти настройки являются общесерверными и применяются ко всем запросам, независимо от обработчика или URL.

С появлением http.ResponseController можно использовать методы SetReadDeadline() и SetWriteDeadline(), чтобы ослабить или, наоборот, ужесточить эти настройки для каждого конкретного запроса в зависимости от ваших потребностей. Например:

func exampleHandler(w http.ResponseWriter, r *http.Request) {
rc := http.NewResponseController(w)

// Установим таймаут записи в 5 секунд.
err := rc.SetWriteDeadline(time.Now().Add(5 * time.Second))
if err != nil {
// Обработка ошибки
}

// Делаем здесь что-нибудь...

// Записываем ответ как обычно
w.Write([]byte("Done!"))
}

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

Несколько деталей, о которых стоит упомянуть:

Если вы установите очень короткий общесерверный таймаут, и этот таймаут будет достигнут до того, как вы вызовете SetWriteDeadline() или SetReadDeadline(), то они не возымеют никакого эффекта. Общесерверный таймаут в этом случае побеждает.

Если базовый http.ResponseWriter не поддерживает установку таймаутов для отдельных запросов, то вызов SetWriteDeadline() или SetReadDeadline() вернет ошибку http.ErrNotSupported.

Теперь можено отменять общесерверный таймаут для отдельных запросов, передав обнуленную структур time.Time в SetWriteDeadlin() или SetReadDeadline(). Например:

rc := http.NewResponseController(w)
err := rc.SetWriteDeadline(time.Time{})
if err != nil {
// Обработка ошибки
}

Интерфейсы Flusher и Hijacker
Тип http.ResponseController также делает более удобным использование «опциональных» интерфейсов http.Flusher и http.Hijacker. Например, до Go 1.20, чтобы отправить данные ответа клиенту, вы могли использовать кода следующего вида:

func exampleHandler(w http.ResponseWriter, r *http.Request) {
f, ok := w.(http.Flusher)
if !ok {
// Обработка ошибки
}

for i := 0; i < 5; i++ {
fmt.Fprintf(w, "Write %d\n", i)
f.Flush()

time.Sleep(time.Second)
}
}

Теперь можно сделать это так:

func exampleHandler(w http.ResponseWriter, r *http.Request) {
rc := http.NewResponseController(w)

for i := 0; i < 5; i++ {
fmt.Fprintf(w, "Write %d\n", i)
err := rc.Flush()
if err != nil {
// Обработка ошибки
}

time.Sleep(time.Second)
}
}
Шаб
лонный код перехвата (hijacking) соединения аналогичен:

func (app *application) home(w http.ResponseWriter, r *http.Request) {
rc := http.NewResponseController(w)

conn, bufrw, err := rc.Hijack()
if err != nil {
// Обработка ошибки
}
defer conn.Close()

// Делаем здесь что-нибудь...
}

Опять же, если ваш базовый http.ResponseWriter не поддерживает flush или перехват соединения, то вызов Flush() или Hijack() в http.ResponseController также вернет ошибку http.ErrNotSupported.

Теперь также проще и безопаснее создавать и использовать пользовательские реализации http.ResponseWriter, которые поддерживают flush и перехват соединения.

Приведите пример с кодом реализации своей http.ResponseWriter в комментариях 👇

@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
👣 Как можно оценить работу кэша? Что можно кэшировать?

Работу кэша можно оценивать при помощи множества метрик разной степени полезности. Перечислим те, которые которые считаю базовыми и наиболее полезными.

Объем памяти, выделенной под кэш. Это базовый показатель, по которому можно судить, сколько используется ресурсов

RPS чтения/записи – количество операций чтения/записи за единицу времени. В обычной ситуации количество операций чтения должно быть в разы больше количества операций записи. Обратное соотношение свидетельствует о проблемах в работе кэша

Количество элементов в кэше. Его полезно знать в дополнение к объему памяти, чтобы обнаруживать большие записи

Hit rate – процент извлечения данных из кэша. Чем он ближе к 100%, тем лучше. Этот параметр буквально определяет то, насколько наш кэш полезен и эффективен

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

Eviction rate – процент вытеснения записей из кэша при достижении лимита используемой памяти. Важный показатель при выборе стратегий вытеснения, о которых мы поговорим чуть позже

Что можно кэшировать?
Строго говоря, кэшировать можно что угодно, но не всегда это целесообразно. Все сильно зависит от данных и паттерна их использования.

Все данные можно условно разделить на 3 группы по частоте изменений:

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

Пример: ошибки (кэширование ошибок может быть настолько важным, что мы посвятили ему целую главу ближе к концу статьи)

Меняются нечасто. Такие данные изменяются в течение минут, часов, дней. Именно в этом случае вы чаще всего задаетесь вопросом “Стоит ли мне кэшировать это?”

Примеры: списки товаров на сайте, описания товаров

Меняются крайне редко или не меняются никогда. Такие данные меняются в течение недель, месяцев и лет. В этом случае данные можно спокойно кэшировать. НО! Ни в коем случае нельзя усыплять бдительность верой в то, что какие-либо данные никогда не изменятся. Рано или поздно они изменятся, поэтому всегда выставляйте всем данным разумный TTL. ВСЕГДА!

Напишите какие стратегии работы с кэшем вы знаете в комментариях 👇

@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
Как управлять сборщиком мусора в GO? Приведите пример с кодом.

Существует параметр, который позволяет управлять сборщиком мусора в Go - это переменная окружения GOGC или ее функциональный аналог SetGCPercent из пакета runtime/debug.

Параметр GOGC определяет процент новой необработанной памяти кучи от живой памяти, при достижении которого будет запущена сборка мусора. Значение GOGC по умолчанию равно 100, что означает, что сборка мусора будет запущена, когда объем новой памяти достигнет 100% от объема живой памяти

Рассмотрим пример программы и отследим изменение размера кучи с помощью инструмента go tool trace. Для запуска программы используем версию Go 1.20.1.

В данном примере, функция performMemoryIntensiveTask использует большое количество памяти размещаемой в куче. Данная функция запускает обработчик с размером очереди NumWorker и количество задач равное NumTasks.

package main

import (
"fmt"
"os"
"runtime/debug"
"runtime/trace"
"sync"
"time"
)

const (
NumWorkers = 4 // Количество воркеров.
NumTasks = 500 // Количество задач.
MemoryIntense = 10000 // Размер память затратной задачи (число элементов).
)

func main() {
// Запись в trace файл.
f, _ := os.Create("trace.out")
trace.Start(f)
defer trace.Stop()

// Установка целевого процента сборщика мусора. По умолчанию 100%.
debug.SetGCPercent(100)

// Очередь задач и очередь результата.
taskQueue := make(chan int, NumTasks)
resultQueue := make(chan int, NumTasks)

// Запуск воркеров.
var wg sync.WaitGroup
wg.Add(NumWorkers)
for i := 0; i < NumWorkers; i++ {
go worker(taskQueue, resultQueue, &wg)
}

// Отправка задач в очередь.
for i := 0; i < NumTasks; i++ {
taskQueue <- i
}
close(taskQueue)

// Получение результатов из очереди.
go func() {
wg.Wait()
close(resultQueue)
}()

// Обработка результатов.
for result := range resultQueue {
fmt.Println("Результат:", result)
}

fmt.Println("Готово!")
}

// Функция воркера.
func worker(tasks <-chan int, results chan<- int, wg *sync.WaitGroup) {
defer wg.Done()

for task := range tasks {
result := performMemoryIntensiveTask(task)
results <- result
}
}

// performMemoryIntensiveTask функция требующая много памяти.
func performMemoryIntensiveTask(task int) int {
// Создание среза большого размера.
data := make([]int, MemoryIntense)
for i := 0; i < MemoryIntense; i++ {
data[i] = i + task
}

// Имитация временной задержки
time.Sleep(10 * time.Millisecond)

// Вычисление результата.
result := 0
for _, value := range data {
result += value
}
return result
}

Для трассировки работы программы результат записывается в файл trace.out:

// Запись в trace файл.
f, _ := os.Create("trace.out")
trace.Start(f)
defer trace.Stop()

Используя инструмент go tool trace, мы можем наблюдать за изменениями размера кучи и анализировать поведение сборщика мусора в вашей программе.

Hidden text
Обратите внимание, что точные детали и возможности инструмента go tool trace могут варьироваться в разных версиях Go, поэтому рекомендуется обратиться к официальной документации для получения более подробной информации о его использовании в вашей конкретной версии Go.

 Статья

@golang_interview
👣 Что выведет код ?

package main

import "fmt"

func sum(a, b any) any {
return a + b
}

func main() {
fmt.Println(sum(2, 3))
}


📌Ответ

@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
👣 Задача. Нам дан связный список целых чисел, отсортированных в порядке возрастания. Наша задача – удалить те узлы, значения которых фигурируют в списке более одного раза. Нужно не только создать список уникальных значений, но и полностью удалить все прочие узлы с этими значениями.

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

Для этого во многих рекурсивных функциях предусмотрены дополнительные аргументы, действующие в качестве флагов в тех ситуациях, когда на предыдущем или последующем уровне выполняются определённые условия.

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


На первый взгляд кажется, что эта задача легко решаема при помощи рекурсивной функции – нужно только обеспечить, чтобы она дошла прямым ходом до конца списка, не пропуская никакого состояния на вышестоящие уровни. Таким образом, первый из вышеперечисленных случаев пропускаем. Во втором случае перезаписываем указатель актуального узла. Третий случай вновь пропускаем. В четвёртом случае ещё раз перезаписываем.

Но задумайтесь, что случится, если вы достигнете конечного (nil), имея строку дублей. Допустим, мы работаем с 2 -> 3 -> 3 -> nil, давайте разберём вышеприведённые случаи узел за узлом:

2 -> 3

2 не равно 3, поэтому здесь мы никаких изменений в узел вносить не будем и вновь вызовем функцию, на этот раз передав ей в качестве аргумента узел '3'.

3 -> 3
a b


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

В данном случае мы можем перезаписать указатель 'a', так, чтобы он стал 'b' – в сущности, удалив 'a'. Теперь снова вызываем функцию, на этот раз передав ей узел 'b'.

3 -> nil
b

Как только мы достигнем nil, имея дубль, нам придётся попытаться перезаписать указатель, но мы не сможем — Go выдаст панику, поскольку нельзя присвоить указателю nil. Поэтому нам понадобится реализовать определённую логику, которая позволяла бы перехватывать ситуацию до возникновения паники, и просто возвращать nil, а не перезаписывать с заменой на nil. Но, если мы вернём nil, то всплывём на уровень вверх. Так мы не только потеряем контекст, позволявший судить, является ли данное значение дублем, но и не сможем изменять значение выше по рекурсивной цепочке, поскольку мы не меняли указатель напрямую.

Вот что можно сделать для решения этой проблемы: мы можем не только полагаться на флаг, сигнализирующий о дубле и посылаемый вниз по пути с рекурсивным изменением. Вдобавок давайте создадим флаг, сигнализирующий, не всплыли ли мы вверх с того уровня, где было дублирующееся значение, смежное с nil. Таким образом, у нас сохранится уловка, позволяющая добраться до nil, и вместе с тем мы сможем безопасно перезаписывать указатели. А если бы мы достигли уровня, имея этот флаг со значением true, мы переключаем узлы и устанавливаем его в false – так сообщается, что дубля в конце у нас уже нет.

📌Решение

@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM