💬 Как можно проверить тип переменной во время выполнения в Go?
В Go мы можем использовать
Рассмотрим пример, где мы проверяем тип переменной
В Go мы можем использовать
switch
для проверки типа переменной во время выполнения. Он называется type switch.Рассмотрим пример, где мы проверяем тип переменной
v
и выполняем определенный набор операций.switch v := param.(type) {
default:
fmt.Printf("Unexpected type %T", v)
case uint64:
fmt.Println("Integer type")
case string:
fmt.Println("String type")
}
👍19🥱1
💬 Есть четыре микросервиса: A → B → C → D. Как обеспечить согласованность данных?
Чтобы обеспечить согласованность данных в такой цепочке микросервисов, мы можем использовать несколько подходов:
1. Распределенный мьютекс для синхронизации доступа к общим ресурсам и предотвращения гонок данных.
2. Распределенные транзакции для обеспечения атомарности операций между микросервисами можно использовать протокол двухфазного коммита (2PC). Это гарантирует, что все изменения будут применены одновременно или откатятся в случае ошибки.
3. Паттерн Saga, который представляет собой набор локальных транзакций. Каждая локальная транзакция обновляет базу данных и публикует сообщение или событие, инициируя следующую локальную транзакцию в саге. Если транзакция завершилась неудачей, например, из-за нарушения бизнес правил, тогда сага запускает компенсирующие транзакции, которые откатывают изменения, сделанные предшествующими локальными транзакциями.
Чтобы обеспечить согласованность данных в такой цепочке микросервисов, мы можем использовать несколько подходов:
1. Распределенный мьютекс для синхронизации доступа к общим ресурсам и предотвращения гонок данных.
2. Распределенные транзакции для обеспечения атомарности операций между микросервисами можно использовать протокол двухфазного коммита (2PC). Это гарантирует, что все изменения будут применены одновременно или откатятся в случае ошибки.
3. Паттерн Saga, который представляет собой набор локальных транзакций. Каждая локальная транзакция обновляет базу данных и публикует сообщение или событие, инициируя следующую локальную транзакцию в саге. Если транзакция завершилась неудачей, например, из-за нарушения бизнес правил, тогда сага запускает компенсирующие транзакции, которые откатывают изменения, сделанные предшествующими локальными транзакциями.
🤔9❤2👍2
Forwarded from Библиотека Go-разработчика | Golang
🏃 Самоучитель по Go для начинающих. Часть 14. Интерфейсы ввода-вывода. Буферизация. Работа с файлами. Пакеты io, bufio, os
В этой статье рассмотрим основные методы ввода-вывода из пакета io, изучим механизм буферизации и его применение в Go, а также разберем, как работать с файлами с помощью пакета os.
👉 Читать гайд
📌 Остальные части в серии:
1. Особенности и сфера применения Go, установка, настройка
2. Ресурсы для изучения Go с нуля
3. Организация кода. Пакеты, импорты, модули. Ввод-вывод текста.
4. Переменные. Типы данных и их преобразования. Основные операторы
5. Условные конструкции if-else и switch-case. Цикл for. Вложенные и бесконечные циклы
6. Функции и аргументы. Области видимости. Рекурсия. Defer
7. Массивы и слайсы. Append и сopy. Пакет slices
8. Строки, руны, байты. Пакет strings. Хеш-таблица (map)
9. Структуры и методы. Интерфейсы. Указатели. Основы ООП
10. Введение в ООП. Наследование, абстракция, полиморфизм, инкапсуляция
11. Обработка ошибок. Паника. Восстановление. Логирование
12. Обобщенное программирование. Дженерики
13. Работа с датой и временем. Пакет time
В этой статье рассмотрим основные методы ввода-вывода из пакета io, изучим механизм буферизации и его применение в Go, а также разберем, как работать с файлами с помощью пакета os.
👉 Читать гайд
📌 Остальные части в серии:
1. Особенности и сфера применения Go, установка, настройка
2. Ресурсы для изучения Go с нуля
3. Организация кода. Пакеты, импорты, модули. Ввод-вывод текста.
4. Переменные. Типы данных и их преобразования. Основные операторы
5. Условные конструкции if-else и switch-case. Цикл for. Вложенные и бесконечные циклы
6. Функции и аргументы. Области видимости. Рекурсия. Defer
7. Массивы и слайсы. Append и сopy. Пакет slices
8. Строки, руны, байты. Пакет strings. Хеш-таблица (map)
9. Структуры и методы. Интерфейсы. Указатели. Основы ООП
10. Введение в ООП. Наследование, абстракция, полиморфизм, инкапсуляция
11. Обработка ошибок. Паника. Восстановление. Логирование
12. Обобщенное программирование. Дженерики
13. Работа с датой и временем. Пакет time
👍3
💬 Как горутина, выполняющая блокирующий системный вызов, перемещается в глобальную очередь готовых к выполнению горутин в рантайме Go. Какие этапы проходят при этом?
Когда горутина выполняет блокирующий системный вызов, она блокируется и рантайм Go временно паркует её, освобождая поток (M) для выполнения других горутин.
После завершения блокирующей операции горутина разблокируется и перемещается в глобальную очередь готовых к выполнению горутин. Планировщик Go затем выбирает горутину из глобальной очереди и назначает её на доступный поток (M) с логическим процессором (P) для продолжения выполнения.
📌 Пример:
Он демонстрирует, как горутина
Когда горутина выполняет блокирующий системный вызов, она блокируется и рантайм Go временно паркует её, освобождая поток (M) для выполнения других горутин.
После завершения блокирующей операции горутина разблокируется и перемещается в глобальную очередь готовых к выполнению горутин. Планировщик Go затем выбирает горутину из глобальной очереди и назначает её на доступный поток (M) с логическим процессором (P) для продолжения выполнения.
📌 Пример:
package main
import (
"fmt"
"net/http"
"time"
)
func main() {
go fetch("http://example.com")
fmt.Println("Fetching started")
time.Sleep(5 * time.Second)
}
func fetch(url string) {
resp, err := http.Get(url)
if err != nil {
fmt.Println("Error fetching URL:", err)
return
}
defer resp.Body.Close()
fmt.Println("Fetched", url, "with status:", resp.Status)
}
Он демонстрирует, как горутина
fetch
блокируется на HTTP-запросе, освобождает поток, и после завершения системного вызова возвращается в глобальную очередь для продолжения выполнения.👍8👾3❤2
💬 Назовите основные юзкейсы для контекста WithoutCancel.
Основные кейсы для использования
1. Фоновые задачи: когда необходимо выполнять фоновую операцию, такую как обновление кеша или периодическая синхронизация данных, важно, чтобы эти процессы не прерывались отменой основного контекста. Использование
2. Логирование и мониторинг: в системах, где логирование критически важно, необходимо гарантировать, что операции записи логов завершатся, даже если родительский контекст был отменен. Это помогает сохранить целостность логов и иметь полную информацию о произошедших событиях.
3. Rollback: в случае возникновения ошибки в транзакции или критической операции необходимо гарантировать, что операции отката будут выполнены полностью, даже если основной контекст был отменен. Это важно для поддержания согласованности данных.
4. Задачи с высоким приоритетом завершения: некоторые задачи должны быть завершены независимо от внешних факторов. Это могут быть операции, связанные с безопасностью, или критически важные операции системы.
Основные кейсы для использования
context.WithoutCancel
(Go 1.21+) включают ситуации, когда необходимо гарантировать завершение определенных операций независимо от отмены родительского контекста. В таких случаях WithoutCancel
помогает изолировать часть логики, которая не должна прерываться. Вот несколько конкретных примеров:1. Фоновые задачи: когда необходимо выполнять фоновую операцию, такую как обновление кеша или периодическая синхронизация данных, важно, чтобы эти процессы не прерывались отменой основного контекста. Использование
WithoutCancel
гарантирует, что такие фоновые задачи продолжат выполнение до завершения.2. Логирование и мониторинг: в системах, где логирование критически важно, необходимо гарантировать, что операции записи логов завершатся, даже если родительский контекст был отменен. Это помогает сохранить целостность логов и иметь полную информацию о произошедших событиях.
3. Rollback: в случае возникновения ошибки в транзакции или критической операции необходимо гарантировать, что операции отката будут выполнены полностью, даже если основной контекст был отменен. Это важно для поддержания согласованности данных.
4. Задачи с высоким приоритетом завершения: некоторые задачи должны быть завершены независимо от внешних факторов. Это могут быть операции, связанные с безопасностью, или критически важные операции системы.
❤12👍7
💬 Нужно ли лочить структуру мьютексом, если идет конкурентная запись в разные поля структуры?
В Go структура данных не является автоматически потокобезопасной. Даже если мы записываем в разные поля структуры из разных горутин, могут возникнуть проблемы с синхронизацией, такие как race conditions.
Основная причина использования мьютексов или других механизмов синхронизации заключается в том, чтобы обеспечить атомарность операций и предотвратить непредсказуемое поведение, которое может возникнуть из-за конкурентного доступа к данным.
Рассмотрим пример структуры с двумя полями, которые обновляются конкурентно:
1. Мы используем мьютекс (
2. Использование мьютекса предотвращает гонки данных и гарантирует корректное обновление полей структуры.
Если известно, что запись в разные поля не приведет к гонкам данных (например, поля абсолютно независимы и нет побочных эффектов), можно обойтись без мьютекса. Однако это решение требует глубокого понимания контекста и потенциальных рисков.
В Go структура данных не является автоматически потокобезопасной. Даже если мы записываем в разные поля структуры из разных горутин, могут возникнуть проблемы с синхронизацией, такие как race conditions.
Основная причина использования мьютексов или других механизмов синхронизации заключается в том, чтобы обеспечить атомарность операций и предотвратить непредсказуемое поведение, которое может возникнуть из-за конкурентного доступа к данным.
Рассмотрим пример структуры с двумя полями, которые обновляются конкурентно:
package main
import (
"fmt"
"sync"
)
type Data struct {
Field1 int
Field2 int
mu sync.Mutex
}
func main() {
d := &Data{}
var wg sync.WaitGroup
// Запись в Field1
wg.Add(1)
go func() {
defer wg.Done()
for i := 0; i < 1000; i++ {
d.mu.Lock()
d.Field1 = i
d.mu.Unlock()
}
}()
// Запись в Field2
wg.Add(1)
go func() {
defer wg.Done()
for i := 0; i < 1000; i++ {
d.mu.Lock()
d.Field2 = i
d.mu.Unlock()
}
}()
wg.Wait()
fmt.Println("Field1:", d.Field1)
fmt.Println("Field2:", d.Field2)
}
1. Мы используем мьютекс (
d.mu.Lock()
и d.mu.Unlock()
) для защиты записи в структуру Data
. Даже если запись происходит в разные поля структуры, мьютекс гарантирует, что только одна горутина имеет доступ к структуре в любой момент времени.2. Использование мьютекса предотвращает гонки данных и гарантирует корректное обновление полей структуры.
Если известно, что запись в разные поля не приведет к гонкам данных (например, поля абсолютно независимы и нет побочных эффектов), можно обойтись без мьютекса. Однако это решение требует глубокого понимания контекста и потенциальных рисков.
👍21❤1
Ответьте на 3 вопроса, чтобы получить вводные занятия к курсу «Алгоритмы и структуры данных»
🔥Получите вводные занятия, ответив на 3 вопроса – https://proglib.io/w/e500f583
На вводной части вас ждут:
1. Лекция «Производительность алгоритмов» от руководителя разработки Яндекс.Самокатов;
2. Лекция «Итеративные сортировки и линейные сортировки» от аспирант департамента искусственного интеллекта ВШЭ;
3. Практические задания после лекций;
4. Ссылки на дополнительные материалы для самостоятельного изучения.
⚡️ Переходите и начинайте учиться уже сегодня – https://proglib.io/w/e500f583
🔥Получите вводные занятия, ответив на 3 вопроса – https://proglib.io/w/e500f583
На вводной части вас ждут:
1. Лекция «Производительность алгоритмов» от руководителя разработки Яндекс.Самокатов;
2. Лекция «Итеративные сортировки и линейные сортировки» от аспирант департамента искусственного интеллекта ВШЭ;
3. Практические задания после лекций;
4. Ссылки на дополнительные материалы для самостоятельного изучения.
Please open Telegram to view this post
VIEW IN TELEGRAM
💬 Как использовать string canonicalization для экономии памяти?
В Go, строки неизменяемы и часто одинаковые строки могут иметь разные блоки памяти. String canonicalization позволяет этим строкам делить один и тот же блок памяти, что может значительно сэкономить память. Рассмотрим два способа реализации на Go.
1️⃣ Первый способ предполагает, что когда две строки равны, одна строка может заменить другую, чтобы они делили одну и ту же память.
2️⃣ Использование
Этот способ более удобен и гибок:
В Go, строки неизменяемы и часто одинаковые строки могут иметь разные блоки памяти. String canonicalization позволяет этим строкам делить один и тот же блок памяти, что может значительно сэкономить память. Рассмотрим два способа реализации на Go.
1️⃣ Первый способ предполагает, что когда две строки равны, одна строка может заменить другую, чтобы они делили одну и ту же память.
func CanonicalizeStrings(ss []string) {
type S struct {
str string
index int
}
var temp = make([]S, len(ss))
for i := range temp {
temp[i] = S {
str: ss[i],
index: i,
}
}
for i := 0; i < len(temp); {
var k = i+1
for j := k; j < len(temp); j++ {
if temp[j].str == temp[i].str {
temp[j].str = temp[i].str
temp[k], temp[j] = temp[j], temp[k]
k++
}
}
i = k
}
for i := range temp {
ss[temp[i].index] = temp[i].str
}
}
2️⃣ Использование
unique.Handle
, представленного в Go 1.23.Этот способ более удобен и гибок:
import "unique"
func CanonicalizeString(s string) string {
return unique.Make(s).Value()
}
func CanonicalizeStrings(ss []string) {
for i, s := range ss {
ss[i] = CanonicalizeString(s)
}
}
👍6❤1
💬 Как итерироваться по рунам в строке Go?
Для получения рун в строке достаточно использовать конструкцию for-range.
Руна может состоять из 1-4 байтов в кодировке UTF-8. Здесь
Для получения рун в строке достаточно использовать конструкцию for-range.
for i, rune := range aString {
... // используем руну
}
Руна может состоять из 1-4 байтов в кодировке UTF-8. Здесь
i
является начальным индексом байтов руны. Если нам нужно не только получить сами руны, но и их длину, удобным способом будет использование пакета unicode/utf8
:import "unicode/utf8"
var str = aString
for {
rune, size := utf8.DecodeRuneInString(str)
if size == 0 {
break
}
str = str[size:]
... // используем руну
}
Конкурс года в «Библиотеке программиста»: смонтируйте короткий вертикальный ролик формата Shorts/Reels* на тему программирования и разработки — лучший автор получит 40 тысяч рублей
Подробные условия:
Какие ролики мы не принимаем:
Таймлайн:
2 августа — заканчиваем принимать видео
⬇️
3 августа — начинаем загружать лучшие видео в инстаграм
⬇️
9 августа — подводим итоги
*Организация Meta признана экстремистской в РФ
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
✍️ «Библиотека программиста» находится в поиске автора на написание книжных рецензий
Кто нужен?
● Энтузиасты (джуны и выше), которые которые разбираются в IT
● Любители книг, которые хотели бы получать деньги за чтение и написание рецензий
● Работаем с самозанятыми (компенсируем налог), ИП
Мы предлагаем частичную занятость и полностью удаленный формат работы — можно совмещать с основной и находиться в любом месте🌴
✉️ Станьте частью нашей команды — присылайте резюме и примеры работ hello@proglib.io
Кто нужен?
● Энтузиасты (джуны и выше), которые которые разбираются в IT
● Любители книг, которые хотели бы получать деньги за чтение и написание рецензий
● Работаем с самозанятыми (компенсируем налог), ИП
Мы предлагаем частичную занятость и полностью удаленный формат работы — можно совмещать с основной и находиться в любом месте🌴
✉️ Станьте частью нашей команды — присылайте резюме и примеры работ hello@proglib.io
💬 Как получить адреса байтов строки в Go?
1. Использование
2. Использование
Начиная с версии Go 1.23, метод
3. Конвертация строки в срез байтов. С версии Go 1.22 была добавлена оптимизация, которая позволяет конвертировать строки в срезы байтов без копирования памяти, если байты не будут изменяться. Это позволяет безопасно получать адреса байтов строки:
1. Использование
unsafe
до версии Go 1.22. До версии Go 1.22 получение адресов байтов строки было возможно только через пакет unsafe
. Это небезопасный способ, но он работает:
package main
import (
"unsafe"
)
func main() {
str := "Hello, Go!"
addr := unsafe.StringData(str)
println(addr, string(*addr))
}
2. Использование
reflect
с версии Go 1.23Начиная с версии Go 1.23, метод
reflect.Value.UnsafePointer
позволяет получить адреса байтов строки:
package main
import (
"reflect"
)
func main() {
str := "Hello, Go!"
addr := (*byte)(reflect.ValueOf(str).UnsafePointer())
println(addr, string(*addr))
}
3. Конвертация строки в срез байтов. С версии Go 1.22 была добавлена оптимизация, которая позволяет конвертировать строки в срезы байтов без копирования памяти, если байты не будут изменяться. Это позволяет безопасно получать адреса байтов строки:
package main
func main() {
str := "Hello, Go!"
byteSlice := []byte(str)
addr := &byteSlice[0]
println(addr, string(*addr))
}
GitHub
cmd/compile: read-only escape analysis and avoiding string -> []byte copies · Issue #2205 · golang/go
Many functions take a []byte but only read from it. If the escape analysis code could flag parameters of a function as read-only, then code which passes in a []byte(string) conversion could be chea...
🔥5👍1
💬 Перечислите все способы клонирования срезов на Go?
1️⃣ Использование make и copy
2️⃣ Более развернутая форма 1 способа
3️⃣ Использование make и append
4️⃣ Использование append
5️⃣ Другой способ использования append
6️⃣ Использование
1️⃣ Использование make и copy
cloned := make(SliceType, len(aSlice))
copy(cloned, aSlice)
2️⃣ Более развернутая форма 1 способа
var cloned SliceType
if aSlice != nil {
cloned = make(SliceType, len(aSlice))
copy(cloned, aSlice)
}
3️⃣ Использование make и append
append(make(SliceType, 0, len(aSlice)), aSlice...)
4️⃣ Использование append
append(SliceType(nil), aSlice...) // Вариант 1
// или
append(SliceType{}, aSlice...) // Вариант 2
5️⃣ Другой способ использования append
append(aSlice[:0:0], aSlice...)
6️⃣ Использование
slices.Clone
(Go 1.21+)
import "slices"
slices.Clone(aSlice)
👍17🥱10
🧑💻 Статьи для IT: как объяснять и распространять значимые идеи
Напоминаем, что у нас есть бесплатный курс для всех, кто хочет научиться интересно писать — о программировании и в целом.
Что: семь модулей, посвященных написанию, редактированию, иллюстрированию и распространению публикаций.
Для кого: для авторов, копирайтеров и просто программистов, которые хотят научиться интересно рассказывать о своих проектах.
👉Материалы регулярно дополняются, обновляются и корректируются. А еще мы отвечаем на все учебные вопросы в комментариях курса.
Напоминаем, что у нас есть бесплатный курс для всех, кто хочет научиться интересно писать — о программировании и в целом.
Что: семь модулей, посвященных написанию, редактированию, иллюстрированию и распространению публикаций.
Для кого: для авторов, копирайтеров и просто программистов, которые хотят научиться интересно рассказывать о своих проектах.
👉Материалы регулярно дополняются, обновляются и корректируются. А еще мы отвечаем на все учебные вопросы в комментариях курса.
💬 Назовите основные способы создания среза в Go.
Предположим, что тип нового создаваемого среза — это
1. Использование составных литералов:
Емкость результирующего среза не всегда выравнивается по размеру класса памяти.
2. Использование встроенной функции
Емкость результирующего среза не всегда выравнивается по размеру класса памяти.
3. Использование встроенных функций
С официальным Go-инструментарием емкость результирующего среза выравнивается по размеру класса памяти.
Предположим, что тип нового создаваемого среза — это
S,
а s0
— это нулевое значение литерала типа S.
1. Использование составных литералов:
// или
var aSlice = S{Len - 1: s0} // длина и емкость равны Len (константа)
Емкость результирующего среза не всегда выравнивается по размеру класса памяти.
2. Использование встроенной функции
make:
// или
var aSlice = make(S, initialLength) // емкость == initialLength
Емкость результирующего среза не всегда выравнивается по размеру класса памяти.
3. Использование встроенных функций
append
+ make
):
// или
var aSlice = append(S(nil), make(S, initialLength)...)
// или
var aSlice = append(S{}, make(S, initialLength, capacity)...)
// или
var aSlice = append(S(nil), make(S, initialLength, capacity)...)
С официальным Go-инструментарием емкость результирующего среза выравнивается по размеру класса памяти.
👍5
💬 Как создать байтовый срез на Go, не обнуляя его байтовые элементы?
До версии Go 1.21 этого добиться было невозможно, даже небезопасными способами. Начиная с Go 1.21, реализация
В большинстве случаев встроенная функция make обнуляет элементы результирующего среза, поэтому она часто сравнительно медленнее.
С реализацией версии 1.21+ у нас появилась возможность создавать байтовые срезы без инициализации их элементов нулем (хотя для достижения этой функциональности требуется использование функций
До версии Go 1.21 этого добиться было невозможно, даже небезопасными способами. Начиная с Go 1.21, реализация
strings.Builder.Grow
вызывает внутреннюю функцию bytealg.MakeNoZero
вместо встроенной функции make
, которую вызывала старая реализация. В большинстве случаев встроенная функция make обнуляет элементы результирующего среза, поэтому она часто сравнительно медленнее.
С реализацией версии 1.21+ у нас появилась возможность создавать байтовые срезы без инициализации их элементов нулем (хотя для достижения этой функциональности требуется использование функций
unsafe
)
import (
"strings"
"unsafe"
)
func MakeDirtyByteSlice(n int) []byte {
var b strings.Builder
b.Grow(n)
var p = unsafe.StringData(b.String())
return unsafe.Slice(p, n)
}
👍9❤3
💬 Назовите основные функции и механизмы, которые могут остановить или приостановить выполнение текущей горутины в Go.
1.
2.
3.
4. Блокировка в канале: горутина приостанавливается, ожидая отправки или получения данных через канал.
5.
6.
1.
time.Sleep(d time.Duration)
: приостанавливает горутину на указанный период времени.2.
runtime.Gosched()
: уступает выполнение другим горутинам, временно приостанавливая текущую.3.
runtime.Goexit()
: немедленно завершает текущую горутину, выполняя все отложенные вызовы.4. Блокировка в канале: горутина приостанавливается, ожидая отправки или получения данных через канал.
5.
sync.Mutex
: горутина блокируется при вызове Lock(),
ожидая освобождения мьютекса.6.
context.Context
: приостанавливает горутину, слушающую канал Done()
, когда контекст отменяется.👍16🔥2
Forwarded from Библиотека девопса | DevOps, SRE, Sysadmin
🍇 18 основных паттернов микросервисной архитектуры
Рассказываем о паттернах, которые представляют собой набор проверенных решений типичных проблем и задач в микросервисной архитектуре. Их правильное применение может значительно улучшить масштабируемость и надежность системы.
Читать статью
Рассказываем о паттернах, которые представляют собой набор проверенных решений типичных проблем и задач в микросервисной архитектуре. Их правильное применение может значительно улучшить масштабируемость и надежность системы.
Читать статью
❤4🥱2
💬 Какие типы оператора defer существуют в Go?
📌 До Go 1.13: heap-allocated defer
До версии Go 1.13 все объекты
📌 Go 1.13: stack-allocated defer
С версии Go 1.13 была введена возможность выделения объектов
📌 Go 1.13+: open-coded defer
Также с версии Go 1.13 была введена оптимизация для встраивания
📌 Go 1.22: heap-allocated defer в циклах
В Go 1.22, если
👉 Подробнее
📌 До Go 1.13: heap-allocated defer
До версии Go 1.13 все объекты
defer
выделялись в куче. Это могло приводить к снижению производительности из-за затрат на выделение памяти в куче.📌 Go 1.13: stack-allocated defer
С версии Go 1.13 была введена возможность выделения объектов
defer
в стеке. Это позволило улучшить производительность за счет уменьшения затрат на выделение памяти.📌 Go 1.13+: open-coded defer
Также с версии Go 1.13 была введена оптимизация для встраивания
defer
в конец функции и перед каждым оператором return
. Это значительно улучшило производительность, но применимо только в ограниченных кейсах, например, когда количество defer-операторов невелико (не более 8) и нет динамических конструкций, таких как циклы.📌 Go 1.22: heap-allocated defer в циклах
В Go 1.22, если
defer
используется внутри цикла, он по-прежнему выделяется в куче, поскольку количество объектов defer
может динамически меняться во время выполнения.👉 Подробнее
👍12
Forwarded from Библиотека девопса | DevOps, SRE, Sysadmin
🌐 9 основных паттернов для проектирования распределенных систем
В этой статье мы рассмотрим 9 основных паттернов и области их применения, что поможет вам в проектировании высоконагруженных приложений.
Читать статью
В этой статье мы рассмотрим 9 основных паттернов и области их применения, что поможет вам в проектировании высоконагруженных приложений.
Читать статью
❤🔥2