Forwarded from easyoffer
Новая фича на easyoffer – Автоотлики
Вы автоматически откликаетесь на подходящие вам вакансии. Попробуйте её бесплатно и начните получать больше предложений о работе.
🚀 Запуск занимаем всего 3 минуты, а экономит очень много времени
🛡 Это безопасно: easyoffer официально одобрен HeadHunter и прошел его модерацию.
🥷🏻 Автоотклик незаметен для рекртера. Автоотклик ничем не отличается от обычного отклика, который вы делаете вручную
Рекрутеры давно используют автоматизацию для поиска кандидатов. Так почему вы должны откликаться вручную?
💡Совет – Добавьте шаблон сопроводительного письма, чтобы откликаться на большее количество вакансий (на некоторые вакансии нельзя откликнуться без сопроводительного)
Попробовать бесплатно → https://easyoffer.ru/autoapply
Вы автоматически откликаетесь на подходящие вам вакансии. Попробуйте её бесплатно и начните получать больше предложений о работе.
🚀 Запуск занимаем всего 3 минуты, а экономит очень много времени
🛡 Это безопасно: easyoffer официально одобрен HeadHunter и прошел его модерацию.
🥷🏻 Автоотклик незаметен для рекртера. Автоотклик ничем не отличается от обычного отклика, который вы делаете вручную
Рекрутеры давно используют автоматизацию для поиска кандидатов. Так почему вы должны откликаться вручную?
💡Совет – Добавьте шаблон сопроводительного письма, чтобы откликаться на большее количество вакансий (на некоторые вакансии нельзя откликнуться без сопроводительного)
Попробовать бесплатно → https://easyoffer.ru/autoapply
🤔2
Карты (maps) реализованы на основе хеш-таблиц, что обеспечивает быстрый доступ к значениям по ключам. Давайте рассмотрим, как происходит поиск по ключу в карте, и какие этапы включены в этот процесс.
Сначала вычисляется хеш-значение ключа. Функция хеширования преобразует ключ в целое число, которое служит индексом в хеш-таблице.
Хеш-значение используется для доступа к соответствующей "ячейке" или "корзине" (bucket) в хеш-таблице.
Если корзина содержит несколько элементов (из-за коллизий хеширования), Go выполняет линейный поиск среди этих элементов, сравнивая ключи с использованием оператора
==.Когда вы пытаетесь получить значение по ключу, Go сначала вычисляет хеш-значение этого ключа. Хеш-функция берет ключ (например, строку или целое число) и преобразует его в индекс хеш-таблицы.
Хеш-значение указывает на конкретную корзину в хеш-таблице. Корзина может содержать один или несколько элементов. В случае коллизий (когда несколько ключей хешируются в один и тот же индекс) корзина может содержать связанный список или другой механизм для хранения нескольких элементов.
Если корзина содержит несколько элементов, Go выполняет линейный поиск среди этих элементов. Для каждого элемента в корзине сравнивается ключ с искомым ключом с использованием оператора
==. Если ключи совпадают, возвращается соответствующее значение. Если ключ не найден, возвращается нулевое значение типа (zero value) и флаг, указывающий на отсутствие ключа.package main
import "fmt"
func main() {
myMap := map[string]int{
"Alice": 25,
"Bob": 30,
}
value, exists := myMap["Alice"]
if exists {
fmt.Println("Alice:", value) // Alice: 25
} else {
fmt.Println("Alice not found")
}
value, exists = myMap["Charlie"]
if exists {
fmt.Println("Charlie:", value)
} else {
fmt.Println("Charlie not found") // Charlie not found
}
}
Даже при хорошей хеш-функции неизбежны коллизии, когда разные ключи хешируются в один и тот же индекс. Эффективно обрабатывает такие случаи, используя корзины для хранения элементов с одинаковыми хеш-значениями.
В среднем, доступ к элементу в карте осуществляется за константное время O(1), что делает карты очень эффективными для поиска по ключу. Однако в худшем случае, при большой нагрузке коллизий, производительность может деградировать до линейного времени O(n).
Карты не являются потокобезопасными. Если одна горутина изменяет карту, в то время как другая горутина читает из нее, это может привести к панике. Для обеспечения потокобезопасности используйте мьютексы или структуру
sync.Map.Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊3
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
В Go срезы (
slice) динамически изменяемы, и при добавлении новых элементов их вместимость (capacity) увеличивается по определённому алгоритму. Когда срезу требуется больше места, чем доступно в его текущей
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
👍2
Такая операция безопасна, но при множественных склеиваниях может быть неэффективной — лучше использовать буферы или массив + .joined().
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊4👍2
Горутины в Go являются легковесными потоками управления, которые позволяют выполнять параллельные задачи более эффективно по сравнению с потоками операционной системы. Их ключевые преимущества включают:
Горутины потребляют значительно меньше памяти и ресурсов по сравнению с потоками ОС. Каждая горутина стартует с размером стека порядка 2 КБ, тогда как поток ОС требует гораздо большего размера стека (обычно несколько мегабайт).
Горутины управляются встроенным планировщиком Go, а не планировщиком ОС. Планировщик Go распределяет выполнение горутин по доступным потокам ОС, используя концепцию "M:N" (много горутин на несколько потоков).
Создание, синхронизация и управление горутинами намного проще благодаря встроенным средствам Go, таким как каналы (
channels) и sync-пакет. В то время как работа с потоками ОС требует дополнительных усилий для синхронизации (мьютексы, условные переменные и т.д.).Стек горутины автоматически увеличивается или уменьшается в зависимости от потребностей, что позволяет эффективно использовать память.
Синтаксис и использование горутин интуитивно понятны. Для запуска достаточно добавить
go перед вызовом функции:package main
import (
"fmt"
"time"
)
func sayHello() {
for i := 0; i < 5; i++ {
fmt.Println("Hello")
time.Sleep(100 * time.Millisecond)
}
}
func main() {
go sayHello() // Запускаем горутину
fmt.Println("World")
time.Sleep(1 * time.Second) // Даем горутине время завершиться
}
Горутины позволяют эффективно использовать современные многоядерные процессоры благодаря встроенной конкурентной модели и поддержке параллелизма.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Go использует кооперативную (мягкую) многозадачность. Это значит, что переключение между горутинами происходит не по системному таймеру, а в определённых точках выполнения, например при вызове функций ввода-вывода, channel-операций или при runtime.Gosched().
До Go 1.14 переключение происходило реже, потому что не было прерываний по таймеру. С Go 1.14+ появилась возможность принудительного прерывания при помощи механизма async preemption (асинхронное вытеснение), что приблизило поведение к "жёсткой" многозадачности, но в рамках кооперативной модели.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3💊1
Слайсы являются ссылочными типами, поэтому простое присваивание одного слайса другому создаст новую ссылку на тот же подлежащий массив. Если вы хотите создать копию слайса с независимым подлежащим массивом, можно использовать встроенную функцию
copy или методы, такие как использование append.Создает побайтовую копию элементов из одного слайса в другой.
package main
import "fmt"
func main() {
original := []int{1, 2, 3, 4, 5}
// Создаем новый слайс той же длины, что и оригинал
copySlice := make([]int, len(original))
// Копируем элементы из оригинального слайса в новый
copy(copySlice, original)
// Изменяем элемент в копии
copySlice[0] = 100
fmt.Println("Оригинал:", original) // Выводит: Оригинал: [1 2 3 4 5]
fmt.Println("Копия:", copySlice) // Выводит: Копия: [100 2 3 4 5]
}
Использование функции
inal)
Чтобы создать новый слайс с копированными элементами.
package main
import "fmt"
func main() {
original := []int{1, 2, 3, 4, 5}
// Копируем элементы из оригинального слайса в новый слайс
copySlice := append([]int(nil), original...)
// Изменяем элемент в копии
copySlice[0] = 100
fmt.Println("Оригинал:", original) // Выводит: Оригинал: [1 2 3 4 5]
fmt.Println("Копия:", copySlice) // Выводит: Копия: [100 2 3 4 5]
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
Создание пакета начинается с указания package packageName в файлах. Импорт пакетов осуществляется через import "packageName". Сторонние пакеты подключаются через Go Modules.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Это функции, которые принимают набор значений и возвращают одно агрегированное значение. В языке Go нет встроенных агрегатных функций, как в SQL, но их можно реализовать самостоятельно.
суммирует все элементы.
вычисляет среднее значение.
находит минимальный элемент.
находит максимальный элемент.
считает количество элементов.
Функция суммы (
SUM) func Sum(nums []int) int {
sum := 0
for _, num := range nums {
sum += num
}
return sum
}Функция среднего (
AVG) func Average(nums []int) float64 {
if len(nums) == 0 {
return 0
}
return float64(Sum(nums)) / float64(len(nums))
}Функция минимума (
MIN) func Min(nums []int) int {
if len(nums) == 0 {
panic("empty slice")
}
min := nums[0]
for _, num := range nums {
if num < min {
min = num
}
}
return min
}Функция максимума (
MAX) func Max(nums []int) int {
if len(nums) == 0 {
panic("empty slice")
}
max := nums[0]
for _, num := range nums {
if num > max {
max = num
}
}
return max
}Функция подсчёта (
COUNT) func Count(nums []int) int {
return len(nums)
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Это индекс, содержащий несколько столбцов. Он используется, когда запрос фильтруется по нескольким полям одновременно.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Это ситуация, когда несколько горутин (или потоков) находятся в состоянии ожидания друг друга, из-за чего выполнение программы останавливается. В Go deadlock может произойти при неправильной работе с каналами, мьютексами и другими механизмами синхронизации.
Если несколько горутин используют блокировки (например, через мьютексы), убедитесь, что все они захватывают их в одном и том же порядке.
func main() {
var mu1, mu2 sync.Mutex
go func() {
mu1.Lock()
defer mu1.Unlock()
mu2.Lock()
defer mu2.Unlock()
}()
go func() {
mu2.Lock()
defer mu2.Unlock()
mu1.Lock()
defer mu1.Unlock()
}()
}Каналы должны всегда иметь возможность отправки и получения данных. Если одна сторона (отправитель или получатель) заблокирована навсегда, возникает deadlock.
func main() {
ch := make(chan int)
ch <- 42 // Deadlock, так как никто не читает из канала
}Каналы нужно закрывать только со стороны отправителя, и только тогда, когда больше не будет отправок данных. Неправильное закрытие или отсутствие закрытия может привести к проблемам, включая deadlock.
ch := make(chan int)
close(ch) // Закрыт слишком рано
ch <- 42 // Паника
Иногда deadlock происходит, если горутина ждет сама себя.
func main() {
ch := make(chan int)
ch <- 1
fmt.Println(<-ch) // Никогда не выполнится
}Go позволяет избегать блокировок с помощью механизма тайм-аутов и оператора
select. Если операция занимает слишком много времени, можно выполнить альтернативное действие.func main() {
ch := make(chan int)
select {
case data := <-ch:
fmt.Println("Получены данные:", data)
case <-time.After(1 * time.Second):
fmt.Println("Тайм-аут, завершение")
}
}Go предоставляет утилиту
go run -race, которая помогает выявлять гонки данных и другие проблемы, связанные с синхронизацией.go run -race main.go
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
Триггер — это автоматическая реакция базы данных на событие (вставку, обновление, удаление).
Он срабатывает при выполнении операций над таблицами и может:
- Проверять условия.
- Модифицировать данные.
- Логировать изменения.
- Вызывать другие действия.
Триггеры позволяют автоматизировать поведение и применять бизнес-логику внутри БД.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Пустой интерфейс
interface{} является универсальным контейнером, который может содержать значение любого типа. Это связано с тем, что в Go любой тип автоматически реализует пустой интерфейс, поскольку в нем нет методов, которые нужно реализовать. числа, строки, булевы значения и т.д.
массивы, срезы, карты, структуры.
функции различных типов.
значения, которые реализуют другие интерфейсы.
Примитивные типы
package main
import "fmt"
func main() {
var i interface{}
i = 42
fmt.Println(i) // Output: 42
i = "hello"
fmt.Println(i) // Output: hello
i = true
fmt.Println(i) // Output: true
}
Композитные типы
package main
import "fmt"
func main() {
var i interface{}
i = []int{1, 2, 3}
fmt.Println(i) // Output: [1 2 3]
i = map[string]int{"one": 1, "two": 2}
fmt.Println(i) // Output: map[one:1 two:2]
type Person struct {
Name string
Age int
}
i = Person{Name: "Alice", Age: 30}
fmt.Println(i) // Output: {Alice 30}
}
Функции
package main
import "fmt"
func main() {
var i interface{}
i = func() {
fmt.Println("Hello from function")
}
if f, ok := i.(func()); ok {
f() // Output: Hello from function
}
}
Другие интерфейсы
package main
import "fmt"
type Stringer interface {
String() string
}
type Person struct {
Name string
Age int
}
func (p Person) String() string {
return fmt.Sprintf("%s (%d years old)", p.Name, p.Age)
}
func main() {
var i interface{}
i = Person{Name: "Alice", Age: 30}
if str, ok := i.(Stringer); ok {
fmt.Println(str.String()) // Output: Alice (30 years old)
}
}
Утверждение типа
package main
import "fmt"
func main() {
var i interface{} = 42
if v, ok := i.(int); ok {
fmt.Println("Integer:", v) // Output: Integer: 42
} else {
fmt.Println("Not an integer")
}
}
Использование
switch для проверки типа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") // Output: String: hello
printType(42) // Output: Integer: 42
printType(true) // Output: Boolean: true
printType(3.14) // Output: Unknown type: float64
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3💊1
Builder – это порождающий паттерн, который используется для пошагового создания сложных объектов. Он удобен, когда объект имеет много параметров и различных конфигураций.
если у объекта много параметров (особенно опциональных).
вместо длинного конструктора с кучей аргументов можно вызывать методы-построители.
Builder позволяет создать объект инициализированным сразу, без изменения его полей после создания.
В Go нет классов, но можно использовать структуры и методы для реализации этого паттерна.
package main
import "fmt"
// Определяем структуру Car
type Car struct {
Brand string
Model string
Color string
Engine string
}
// Определяем "Строителя" для Car
type CarBuilder struct {
car Car
}
// Методы для пошаговой настройки машины
func (cb *CarBuilder) SetBrand(brand string) *CarBuilder {
cb.car.Brand = brand
return cb
}
func (cb *CarBuilder) SetModel(model string) *CarBuilder {
cb.car.Model = model
return cb
}
func (cb *CarBuilder) SetColor(color string) *CarBuilder {
cb.car.Color = color
return cb
}
func (cb *CarBuilder) SetEngine(engine string) *CarBuilder {
cb.car.Engine = engine
return cb
}
// Метод для финальной сборки объекта
func (cb *CarBuilder) Build() Car {
return cb.car
}
// Используем Builder
func main() {
car := CarBuilder{}.
SetBrand("Tesla").
SetModel("Model S").
SetColor("Red").
SetEngine("Electric").
Build()
fmt.Printf("Car: %+v\n", car)
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
Ext4 — это журналируемая файловая система, используемая в Linux.
Особенности:
- Inode-структура для хранения метаданных файлов.
- Журналирование для устойчивости к сбоям (записывает действия перед применением).
- Extents — последовательные блоки, экономящие место.
- Поддержка больших файлов и томов, дефрагментация, метки времени в наносекундах.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Это версии протокола HTTP, каждая из которых имеет свои особенности и улучшения по сравнению с предыдущими версиями. Важные различия между этими версиями включают следующие аспекты:
Поддерживает одновременное открытие нескольких TCP соединений (обычно 6-8), что позволяет загружать несколько ресурсов параллельно. Однако каждое соединение может обрабатывать только один запрос за раз, что приводит к задержкам из-за блокировки очереди (head-of-line blocking).
Вводит мультиплексирование, позволяющее отправлять множество запросов и ответов асинхронно через одно единственное TCP соединение. Это значительно уменьшает задержки и улучшает производительность при загрузке страниц с большим количеством ресурсов.
Является текстовым протоколом, что означает, что запросы и ответы форматируются в виде читаемого текста.
Бинарный протокол, который делает передачу данных более эффективной и менее подверженной ошибкам в синтаксическом анализе. Бинарный формат упрощает реализацию парсеров и уменьшает размер передаваемых данных.
Заголовки передаются без сжатия, что может привести к значительному объему передаваемых данных, особенно если одни и те же заголовки отправляются повторно с каждым запросом.
Использует механизм сжатия заголовков HPACK, который уменьшает избыточность заголовков, сжимая их перед отправкой. Это особенно эффективно для повторяющихся запросов к одним и тем же серверам.
Не поддерживает приоритизацию запросов, из-за чего браузеры должны использовать эвристики для управления приоритетами ресурсов.
Поддерживает явную приоритизацию запросов, позволяя клиенту указывать приоритет обработки ресурсов, что делает загрузку страниц более эффективной.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
- Rebase «переписывает» историю, перенося коммиты одной ветки поверх другой, будто они создавались последовательно.
Merge — безопаснее и прозрачно показывает, где ветки сливались. Rebase — чище история, но может быть опасен при совместной разработке, особенно на уже опубликованных ветках.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Паттерн "Очередь" (Queue) в программировании обычно представляет собой структуру данных или архитектурный подход, используемый для обработки сообщений, задач или запросов в определённом порядке (обычно FIFO — "первым вошёл, первым вышел").
Но если рассматривать количество очередей в каком-то конкретном паттерне проектирования, то всё зависит от контекста. Например:
Все задачи обрабатываются в одной очереди по порядку. Используется, когда важен строгий порядок выполнения. Пример: канал (channel) в Go, куда отправляются задачи для обработки.
Используется, когда разные типы задач требуют разной обработки. Например, очереди с разными приоритетами (высокий, средний, низкий). В многопоточных системах каждая очередь может обслуживаться отдельным worker'ом.
Есть одна очередь задач, но несколько рабочих (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
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1