Оптимизируйте множественные вызовы с помощью singleflight
Допустим, у вас есть функция, которая получает данные по сети или выполняет ввод-вывод, и её выполнение занимает около 3 секунд:
Эта функция выдаёт новое значение каждые 10 секунд.
🔹 Если вызвать эту функцию 3 раза подряд, общее время ожидания составит примерно 9 секунд.
🔹 Если использовать 3 горутины, общее время ожидания может сократиться до 3 секунд, но функция всё равно будет вызвана 3 раза для получения одного и того же результата (~99%).
Здесь на помощь приходит пакет singleflight, который доступен по ссылке:
👉 golang.org/x/sync/singleflight.
Этот пакет позволяет выполнить функцию только один раз, независимо от того, сколько раз она была вызвана за эти 3 секунды, и возвращает один общий результат.
Это отлично подходит для оптимизации функций, работающих долго или потребляющих много ресурсов.
Как это работает?
1️⃣ Создаём объект
2️⃣ Передаём в метод
Метод
Параметр
Зачем нужен аргумент
Ключ (
Если поступает несколько запросов с одним и тем же ключом,
Пример работы: Go Playground
С этим подходом, если функция вызывается несколько раз одновременно, фактически выполняется только один её вызов. А результат этого единственного вызова разделяется между всеми ожидающими
👉 @juniorGolang | #tip
Допустим, у вас есть функция, которая получает данные по сети или выполняет ввод-вывод, и её выполнение занимает около 3 секунд:
Эта функция выдаёт новое значение каждые 10 секунд.
Здесь на помощь приходит пакет singleflight, который доступен по ссылке:
Этот пакет позволяет выполнить функцию только один раз, независимо от того, сколько раз она была вызвана за эти 3 секунды, и возвращает один общий результат.
Это отлично подходит для оптимизации функций, работающих долго или потребляющих много ресурсов.
Как это работает?
singleflight.Groupgroup.Do() функцию, которая выполняет дорогостоящие вычисления.Метод
group.Do() возвращает:(result any, err error, shared bool)
Параметр
shared показывает, был ли результат разделён между несколькими вызовами.Зачем нужен аргумент
key?Ключ (
key) — это идентификатор запроса.Если поступает несколько запросов с одним и тем же ключом,
singleflight понимает, что они запрашивают одно и то же.Пример работы: Go Playground
С этим подходом, если функция вызывается несколько раз одновременно, фактически выполняется только один её вызов. А результат этого единственного вызова разделяется между всеми ожидающими
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤2
Фильтрация без лишних аллокаций
При фильтрации слайсов в Go стандартный подход — создание нового слайса для отфильтрованных элементов.
Однако такой метод приводит к дополнительным аллокациям памяти.
Более эффективный способ — фильтровать «на месте», используя исходный массив слайса.
Как это работает:
•
• Добавляя
Таким образом, мы не выделяем новую память, а заполняем уже существующий массив.
Этот метод особенно полезен, когда:
•
• Критична производительность, особенно при работе с большими объемами данных.
👉 @juniorGolang | #tip
При фильтрации слайсов в Go стандартный подход — создание нового слайса для отфильтрованных элементов.
Однако такой метод приводит к дополнительным аллокациям памяти.
Более эффективный способ — фильтровать «на месте», используя исходный массив слайса.
Как это работает:
•
filtered := numbers[:0] создаёт новый слайс filtered, который ссылается на тот же массив, что и numbers, но имеет нулевую длину, сохраняя емкость numbers. • Добавляя
num в filtered, мы избегаем лишних аллокаций, так как просто изменяем содержимое numbers (или его базового массива).Таким образом, мы не выделяем новую память, а заполняем уже существующий массив.
Этот метод особенно полезен, когда:
•
numbers больше не нужен после фильтрации.• Критична производительность, особенно при работе с большими объемами данных.
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
👍16🔥7❤3👎1
Продолжайте работу с контекстами с помощью
Мы уже знаем, что при отмене родительского контекста отменяются и все его дочерние контексты, верно?
Но иногда это поведение нежелательно.
Есть случаи, когда нужно, чтобы определённые операции продолжались, даже если родительский контекст был отменён.
Представьте, что вы обрабатываете HTTP-запрос, и при его отмене (например, из-за таймаута или разрыва соединения с клиентом) всё ещё необходимо залогировать информацию о запросе и собрать метрики.
Это возможно, но новый контекст не содержит значений из исходного контекста события, которые важны, например, для логирования или сбора метрик.
Передача значений возможна только через дочерний контекст.
Возвращаясь к примеру с HTTP-запросом, решение следующее:
Кстати, эта функция появилась в Go 1.21.✌️
👉 @juniorGolang | #tip
context.WithoutCancel()Мы уже знаем, что при отмене родительского контекста отменяются и все его дочерние контексты, верно?
Но иногда это поведение нежелательно.
Есть случаи, когда нужно, чтобы определённые операции продолжались, даже если родительский контекст был отменён.
Представьте, что вы обрабатываете HTTP-запрос, и при его отмене (например, из-за таймаута или разрыва соединения с клиентом) всё ещё необходимо залогировать информацию о запросе и собрать метрики.
«Ха, просто создам новый контекст для этих операций»
Это возможно, но новый контекст не содержит значений из исходного контекста события, которые важны, например, для логирования или сбора метрик.
Передача значений возможна только через дочерний контекст.
Возвращаясь к примеру с HTTP-запросом, решение следующее:
context.WithoutCancel позволяет создать дочерний контекст, который не будет отменён при отмене родительского. Это обеспечивает завершение нужных операций даже после отмены запроса.Кстати, эта функция появилась в Go 1.21.
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11❤3
Избегайте использования
Когда вы работаете над проектами, где нужно генерировать ключи — например, для шифрования или создания уникальных идентификаторов — качество и безопасность этих ключей имеют большое значение.
Почему не
Пакет
Это означает, что если известен способ генерации чисел (seed), можно предсказать их значения.
Даже если использовать в качестве seed текущее время (например,
Почему
Пакет
Он спроектирован таким образом, чтобы быть непредсказуемым, используя источники случайности от операционной системы, которые гораздо сложнее предугадать.
👉 @juniorGolang | #tip
math/rand, вместо этого используйте crypto/rand для генерации ключейКогда вы работаете над проектами, где нужно генерировать ключи — например, для шифрования или создания уникальных идентификаторов — качество и безопасность этих ключей имеют большое значение.
Почему не
math/rand?Пакет
math/rand генерирует псевдослучайные числа.Это означает, что если известен способ генерации чисел (seed), можно предсказать их значения.
Даже если использовать в качестве seed текущее время (например,
time.Now().UnixNano()), уровень непредсказуемости (энтропии) остаётся низким, потому что время между запусками не сильно отличается.Почему
crypto/rand?Пакет
crypto/rand предоставляет способ генерации криптографически стойких случайных чисел.Он спроектирован таким образом, чтобы быть непредсказуемым, используя источники случайности от операционной системы, которые гораздо сложнее предугадать.
crypto/rand подходит для шифрования, аутентификации и других операций, чувствительных к безопасности.Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
👍20❤2
Получаем указатели проще с помощью дженериков
Небольшой совет для тех, кто пишет на Go и часто сталкивается с необходимостью получить указатель на значение.
Раньше вы, возможно, делали так (1 картинка)
Или пытались уместить всё в одну строчку, используя небольшой трюк (2 картинка)
Такой способ рабочий, но выглядит громоздко, особенно если приходится делать это часто для разных типов данных.
Теперь давайте посмотрим на более простой и современный способ с дженериками (3 картинка)
Эта небольшая функция позволяет создавать указатель для любого типа значения, не повторяя один и тот же код снова и снова.
Просто передайте своё значение в функцию
Такой подход делает код чище и избавляет от лишнего дублирования.
Больше не нужно писать отдельные функции или вручную обрабатывать указатели для каждого типа. Держите код простым и сосредоточьтесь на главном.
👉 @juniorGolang | #tip
Небольшой совет для тех, кто пишет на Go и часто сталкивается с необходимостью получить указатель на значение.
Раньше вы, возможно, делали так (1 картинка)
Или пытались уместить всё в одну строчку, используя небольшой трюк (2 картинка)
Такой способ рабочий, но выглядит громоздко, особенно если приходится делать это часто для разных типов данных.
Теперь давайте посмотрим на более простой и современный способ с дженериками (3 картинка)
Эта небольшая функция позволяет создавать указатель для любого типа значения, не повторяя один и тот же код снова и снова.
Просто передайте своё значение в функцию
Ptr, и вы получите нужный указатель (4 картинка)Такой подход делает код чище и избавляет от лишнего дублирования.
Больше не нужно писать отдельные функции или вручную обрабатывать указатели для каждого типа. Держите код простым и сосредоточьтесь на главном.
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
❤10👍6👎4🤔2
Писать неудобный код — сложно, но приходится
Когда я делаю ревью, я всегда убеждаю писать такой код, который может быть неудобно реализовывать, но который удобно использовать и сложно неправильно применить.
Например, чтобы обновить документ в базе данных — почему бы просто не передать клиенту
Лично я такое не приемлю, особенно в командной работе.
Я бы ограничил, какие поля может обновлять клиент, и валидировал бы всё перед записью в базу, даже если для этого нужно больше кода, больше функций.
Я не хочу давать клиенту лишние шансы ошибиться.
Использование
Я не утверждаю, что всё это всегда плохо — как обычно, всё зависит от контекста.
Опытные разработчики знают, когда можно отойти от правил, но никогда не злоупотребляют этим.
Каждая из этих практик сама по себе может быть не критична, но в сумме они могут привести к серьёзным проблемам в будущем.
👉 @juniorGolang | #tip
Когда я делаю ревью, я всегда убеждаю писать такой код, который может быть неудобно реализовывать, но который удобно использовать и сложно неправильно применить.
Например, чтобы обновить документ в базе данных — почему бы просто не передать клиенту
map[string]any, чтобы он сам обновил, что ему нужно?Лично я такое не приемлю, особенно в командной работе.
Я бы ограничил, какие поля может обновлять клиент, и валидировал бы всё перед записью в базу, даже если для этого нужно больше кода, больше функций.
Я не хочу давать клиенту лишние шансы ошибиться.
Использование
any, map[string]interface{}, возврат пустых error, глобальные переменные, сваливание всех хелперов в shared/utils, создание монолитных общих интерфейсов, игнорирование ошибок (или возвращаемых значений) без комментариев, написание огромных функций без разбиения, использование context.Background() ради скорости, возврат 3–4 значений, хардкод вместо констант, наплевательское отношение к дизайну и зависимостям между пакетами, сваливание всех моделей в models/model.go…Я не утверждаю, что всё это всегда плохо — как обычно, всё зависит от контекста.
Опытные разработчики знают, когда можно отойти от правил, но никогда не злоупотребляют этим.
Каждая из этих практик сама по себе может быть не критична, но в сумме они могут привести к серьёзным проблемам в будущем.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14❤7🔥3
Mutex — это концепт, который часто вызывает путаницу, хотя на самом деле он довольно прост для понимания. Mutex — это примитив синхронизации, который помогает избежать гонки данных (race conditions).
Предположим, вы обрабатываете директорию с файлами и хотите сохранить результат обработки каждого файла в срез. Чтобы избежать ситуации, когда несколько горутин одновременно пытаются записать данные в срез, необходимо синхронизировать доступ к нему.
Mutex (сокращение от mutual exclusion) предоставляет две основные функции —
🔹
🔹
Когда первая горутина вызывает
По сути, mutex обеспечивает, чтобы только один поток выполнения (в данном случае — горутина) имел доступ к общему ресурсу в определённый момент времени. Это особенно полезно, когда, например, нужно избежать ситуации, при которой две горутины одновременно записывают данные в одну и ту же позицию среза.
Семафор — это расширение концепции mutex. Если mutex допускает только одного "оператора", то семафор может иметь произвольное количество слотов. Например,
На картинке приведён пример кастомной реализации mutex
👉 @juniorGolang | #tip
Предположим, вы обрабатываете директорию с файлами и хотите сохранить результат обработки каждого файла в срез. Чтобы избежать ситуации, когда несколько горутин одновременно пытаются записать данные в срез, необходимо синхронизировать доступ к нему.
Mutex (сокращение от mutual exclusion) предоставляет две основные функции —
Lock и Unlock, а также внутренний счётчик.counter = 0 означает, что mutex разблокированcounter = 1 означает, что mutex заблокированКогда первая горутина вызывает
Lock, она блокирует доступ к срезу, установив счётчик в 1. Следующая горутина, попытавшаяся войти, будет заблокирована (впадёт в ожидание), пока счётчик не станет равным 0 — то есть пока первая горутина не вызовет Unlock.По сути, mutex обеспечивает, чтобы только один поток выполнения (в данном случае — горутина) имел доступ к общему ресурсу в определённый момент времени. Это особенно полезно, когда, например, нужно избежать ситуации, при которой две горутины одновременно записывают данные в одну и ту же позицию среза.
Семафор — это расширение концепции mutex. Если mutex допускает только одного "оператора", то семафор может иметь произвольное количество слотов. Например,
sync.WaitGroup в Go использует семафорную модель под капотом.На картинке приведён пример кастомной реализации mutex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9❤7
До сих пор самым впечатляющим аспектом Go для меня остаются его примитивы конкурентности.
В традиционных языках программирования конкурентность достигается за счёт потоков операционной системы. Например, если у вас CPU с 6 ядрами и поддержкой 2 логических потоков на ядро, вы можете распараллелить выполнение максимум на 12 потоков.
В Go есть собственный планировщик задач, встроенный в рантайм. Он не зависит от потоков ОС для запуска задач.
Этот лёгкий, пользовательский планировщик работает в пространстве пользователя и позволяет запускать тысячи или даже миллионы конкурентных путей исполнения без накладных расходов, связанных с потоками ОС.
Когда вы запускаете горутину с помощью ключевого слова
Вызов функции и весь его контекст размещаются на этом стеке, создавая независимый путь исполнения, который может выполняться параллельно с другими горутинами.
Синхронизировать горутины можно через
Именно такая лёгкая модель выделения памяти и позволяет Go эффективно управлять тысячами и миллионами конкурентных задач.
👉 @juniorGolang | #tip
В традиционных языках программирования конкурентность достигается за счёт потоков операционной системы. Например, если у вас CPU с 6 ядрами и поддержкой 2 логических потоков на ядро, вы можете распараллелить выполнение максимум на 12 потоков.
В Go есть собственный планировщик задач, встроенный в рантайм. Он не зависит от потоков ОС для запуска задач.
Этот лёгкий, пользовательский планировщик работает в пространстве пользователя и позволяет запускать тысячи или даже миллионы конкурентных путей исполнения без накладных расходов, связанных с потоками ОС.
Когда вы запускаете горутину с помощью ключевого слова
go перед вызовом функции, рантайм Go выделяет небольшой участок памяти под стек этой горутины.Вызов функции и весь его контекст размещаются на этом стеке, создавая независимый путь исполнения, который может выполняться параллельно с другими горутинами.
Синхронизировать горутины можно через
sync.WaitGroup или использовать каналы (chan) для обмена данными и координации между ними.Именно такая лёгкая модель выделения памяти и позволяет Go эффективно управлять тысячами и миллионами конкурентных задач.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤10🔥2👍1
Каналы в Go — это интересная и поначалу достаточно сложная для понимания концепция.
Если вы пришли из традиционных языков, таких как Java, то вы, скорее всего, использовали потоки с мьютексами или асинхронные очереди вроде Apache Kafka для организации взаимодействия между разными частями программы.
Синтаксис каналов может показаться странным, а семантика — расплывчатой, но они становятся понятнее, если воспринимать их как конкурентные очереди.
Канал в Go — это просто очередь, через которую можно передавать данные.
Если вы записываете в канал — это эквивалентно добавлению элемента в очередь.
Если вы читаете из канала — вы извлекаете первый элемент из очереди.
Очередь работает по принципу FIFO (первым пришёл — первым вышел).
У каналов множество применений. Например, представьте, что у вас есть HTTP-обработчик, и после завершения его работы вы хотите запустить дополнительную вычислительную задачу. Вы могли бы использовать асинхронную очередь для запуска workflow, а можете — канал.
В коде вы просто записываете значение в канал, например
Каналы безопасны для конкурентного чтения и записи — для записи в канал не нужен мьютекс.
Вы можете контролировать, сколько сигналов канал может буферизовать, указывая размер буфера при создании через
Можно создать канал, принимающий единичный сигнал (например, сигнал для закрытия базы данных), или буферизированный канал, в который несколько горутин записывают результаты своей работы.
Если количество горутин, пытающихся записать в канал, превышает его ёмкость, то лишние горутины будут блокироваться (то есть ждать), пока данные не будут считаны и не освободится место.
Из-за такого блокирующего поведения каналы иногда используют в качестве семафоров (хотя для этого существуют специализированные пакеты, которые лучше использовать вместо каналов).
По сути, каналы — это отличный способ организации взаимодействия между последовательными процессами, что и составляет суть модели конкурентности CSP (Communicating Sequential Processes) в Go.
Вот пример кода, показывающий мощь каналов. В Go существует возможность подписаться на сигналы операционной системы через канал.
Например, если программа завершается, вызывает панику, прерывается или останавливается — мы можем получить сообщение об этом через канал и корректно завершить работу сервера (записать состояние в лог, закрыть подключения к БД, завершить файловые операции и т. д.).
👉 @juniorGolang | #tip
Если вы пришли из традиционных языков, таких как Java, то вы, скорее всего, использовали потоки с мьютексами или асинхронные очереди вроде Apache Kafka для организации взаимодействия между разными частями программы.
Синтаксис каналов может показаться странным, а семантика — расплывчатой, но они становятся понятнее, если воспринимать их как конкурентные очереди.
Канал в Go — это просто очередь, через которую можно передавать данные.
Если вы записываете в канал — это эквивалентно добавлению элемента в очередь.
Если вы читаете из канала — вы извлекаете первый элемент из очереди.
Очередь работает по принципу FIFO (первым пришёл — первым вышел).
У каналов множество применений. Например, представьте, что у вас есть HTTP-обработчик, и после завершения его работы вы хотите запустить дополнительную вычислительную задачу. Вы могли бы использовать асинхронную очередь для запуска workflow, а можете — канал.
В коде вы просто записываете значение в канал, например
struct{user userID; data interface{}}, и это может послужить триггером для запуска другой вычислительной задачи.Каналы безопасны для конкурентного чтения и записи — для записи в канал не нужен мьютекс.
Вы можете контролировать, сколько сигналов канал может буферизовать, указывая размер буфера при создании через
make (например, userChan := make(chan interface{}, capacity)).Можно создать канал, принимающий единичный сигнал (например, сигнал для закрытия базы данных), или буферизированный канал, в который несколько горутин записывают результаты своей работы.
Если количество горутин, пытающихся записать в канал, превышает его ёмкость, то лишние горутины будут блокироваться (то есть ждать), пока данные не будут считаны и не освободится место.
Из-за такого блокирующего поведения каналы иногда используют в качестве семафоров (хотя для этого существуют специализированные пакеты, которые лучше использовать вместо каналов).
По сути, каналы — это отличный способ организации взаимодействия между последовательными процессами, что и составляет суть модели конкурентности CSP (Communicating Sequential Processes) в Go.
Вот пример кода, показывающий мощь каналов. В Go существует возможность подписаться на сигналы операционной системы через канал.
Например, если программа завершается, вызывает панику, прерывается или останавливается — мы можем получить сообщение об этом через канал и корректно завершить работу сервера (записать состояние в лог, закрыть подключения к БД, завершить файловые операции и т. д.).
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6👍4
Начиная с Go 1.8, стандартная библиотека
Этот метод обеспечивает "плавное" завершение работы сервера, поскольку он:
🔹 Немедленно прекращает приём новых подключений
🔹 Сохраняет существующие подключения активными до завершения всех текущих запросов
🔹 Предоставляет контекст для управления таймаутом на завершение "висящих" запросов
🔹 Завершается только после обработки всех находящихся в процессе выполнения запросов
🔹 Предотвращает утечки подключений при завершении работы приложения
Без корректного завершения, принудительное завершение работы сервера может привести к обрыву активных запросов, ошибкам на стороне клиента и потенциальной неконсистентности данных.
Метод
👉 @juniorGolang | #tip
http.Server включает метод Shutdown, который критически важен для корректного завершения работы веб-сервисов.Этот метод обеспечивает "плавное" завершение работы сервера, поскольку он:
Без корректного завершения, принудительное завершение работы сервера может привести к обрыву активных запросов, ошибкам на стороне клиента и потенциальной неконсистентности данных.
Метод
Shutdown элегантно решает эту проблему. Ниже приведена полноценная реализация, демонстрирующая обработку нескольких сигналов ОС для плавного завершения работы:Please open Telegram to view this post
VIEW IN TELEGRAM
❤14👍9
При разработке приложений, использующих LLM API, таких как OpenAI или Anthropic, вы быстро упрётесь в лимиты запросов при масштабной обработке. Пакет
🔹 Реализует лимитирование скорости на основе алгоритма token bucket с точной настройкой
🔹 Поддерживает ожидание с учётом контекста и возможностью отмены
🔹 Эффективно обрабатывает всплески нагрузки благодаря настраиваемому размеру «ведра»
🔹 Органично интегрируется с моделью конкурентности Go
Алгоритм token bucket особенно хорошо подходит для LLM‑API. Каждый запрос потребляет один токен из «бакета», который пополняется с заданной скоростью. Если «бакет» пуст, запросы ожидают появления токенов вместо того, чтобы сразу завершаться с ошибкой.
Выше приведён пример кода с неофициальным клиентом OpenAI и использованием пакета
👉 @juniorGolang | #tip
rate из https://golang.org — хорошее решение этой проблемы, так как он:Алгоритм token bucket особенно хорошо подходит для LLM‑API. Каждый запрос потребляет один токен из «бакета», который пополняется с заданной скоростью. Если «бакет» пуст, запросы ожидают появления токенов вместо того, чтобы сразу завершаться с ошибкой.
Выше приведён пример кода с неофициальным клиентом OpenAI и использованием пакета
ratePlease open Telegram to view this post
VIEW IN TELEGRAM
👍7❤1
Если вы обрабатываете HTTP-запросы на Go, сжатие gzip является важным инструментом для снижения потребления трафика и ускорения отклика. Пакет
При реализации gzip-сжатия в веб-серверах:
🔹 Всегда проверяйте заголовок
🔹 Устанавливайте корректный заголовок ответа
🔹 Избегайте сжатия уже сжатых форматов (например, изображений, видео)
🔹 Используйте
Вот пример реализации с использованием паттерна middleware.
👉 @juniorGolang | #tip
encoding/gzip упрощает реализацию, но есть некоторые подводные камни, о которых стоит помнить.При реализации gzip-сжатия в веб-серверах:
Accept-Encoding перед применением сжатияContent-Encodingsync.Pool для повторного использования gzip-писателей и повышения производительностиВот пример реализации с использованием паттерна middleware.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14❤2
Начиная с Go 1.26 можно передавать выражение в
До Go 1.26 можно было делать только
Новое поведение
Но есть 3 нюанса:
1. Если
2.
3. Нетипизированные константы сначала получают тип по умолчанию:
👉 @GolangPortal | #tip by Phuong Le
new(), чтобы напрямую получить указатель на результат этого выражения.До Go 1.26 можно было делать только
new(Type), чтобы получить указатель на нулевое значение этого типа.Новое поведение
new(expr) — это синтаксический сахар для:tmp := expr
result := &tmp
Но есть 3 нюанса:
1. Если
expr уже является указателем, то new(expr) вернёт указатель на этот указатель.new(bytes.NewBuffer(nil)) → **bytes.Buffer
2.
new(expr) копирует значение expr во вновь созданную переменную.i := 1
p := new(i)
i = 2 // → *p всё ещё равно 1
3. Нетипизированные константы сначала получают тип по умолчанию:
new(123) → *int
new(1.2) → *float64
new('a') → *rune
new(nil) — невалидно.Please open Telegram to view this post
VIEW IN TELEGRAM
👍13🔥2🤔1
Многие статьи заставляют думать, что
Go держит пул переиспользуемых горутин на каждый процессор и глобальный пул, причём у части горутин в этих пулах уже есть прикреплённые стеки, а у части нет (см. диаграмму ниже)
Рантайм поддерживает динамический “начальный размер стека”, который пересчитывается в каждом цикле сборки мусора исходя из среднего использования стека всеми просканированными горутинами
Когда вы вызываете
Если размер стека повторно используемой горутины отличается от текущего “начального размера стека”, её старый стек освобождается, и ей выделяется новый стек ровно текущего начального размера.
Если подходящей повторно используемой горутины нет, рантайм выделяет новую с фиксированным начальным стеком 2 KiB ( на большинстве 64-битных Unix-подобных платформ)
👉 @GolangPortal #tip by Phuong Le
go func() {} всегда создаёт горутину с 2 KiB стека. Иногда так и есть, но далеко не всегда.Go держит пул переиспользуемых горутин на каждый процессор и глобальный пул, причём у части горутин в этих пулах уже есть прикреплённые стеки, а у части нет (см. диаграмму ниже)
Рантайм поддерживает динамический “начальный размер стека”, который пересчитывается в каждом цикле сборки мусора исходя из среднего использования стека всеми просканированными горутинами
Когда вы вызываете
go f(), рантайм может взять горутину и стек из пулов, поэтому многие горутины на самом деле стартуют с размером стека больше фиксированного минимума 2 KiB – например, 4 KiB, 8 KiB, 16 KiB и так далее.Если размер стека повторно используемой горутины отличается от текущего “начального размера стека”, её старый стек освобождается, и ей выделяется новый стек ровно текущего начального размера.
Если подходящей повторно используемой горутины нет, рантайм выделяет новую с фиксированным начальным стеком 2 KiB ( на большинстве 64-битных Unix-подобных платформ)
Please open Telegram to view this post
VIEW IN TELEGRAM
❤13👍9👎1
Разработчиков на Go можно условно разделить на три группы
1. Тех, кто использует
2. Тех, кто использует
3. Тех, кто использует
Оказалось, что последняя группа была недовольна, потому что иногда нужно отменить родительский контекст, не затрагивая дочерний, но при этом сохранить в дочернем все значения из родительского контекста.
Чтобы поддержать этот чудесный сценарий, в Go 1.21 добавили функцию
Context в Go начинался как простая, уродливая концепция. Со временем он превратился в сложную, уродливую
👉 @GolangPortal #tip by Anton Zhiyanov
1. Тех, кто использует
context для отмены.2. Тех, кто использует
context для прокидывания значений.3. Тех, кто использует
context и для отмены, и для передачи значений.Оказалось, что последняя группа была недовольна, потому что иногда нужно отменить родительский контекст, не затрагивая дочерний, но при этом сохранить в дочернем все значения из родительского контекста.
Чтобы поддержать этот чудесный сценарий, в Go 1.21 добавили функцию
context.WithoutCancel. Она отвязывает дочерний контекст от родительского в части отмены, но сохраняет все значения.Context в Go начинался как простая, уродливая концепция. Со временем он превратился в сложную, уродливую
Please open Telegram to view this post
VIEW IN TELEGRAM
❤14👍10😁9