Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔2👀2
Контексты (
context.Context
) широко используются для передачи метаданных, управления временем выполнения и отмены операций в многопоточной среде. Пакет context
предоставляет несколько видов контекстов, каждый из которых предназначен для различных сценариев использования. context.Background()
возвращает пустой контекст, который обычно используется как корневой контекст в программах. Он не имеет отмены или дедлайна и не содержит значений.ctx := context.Background()
context.TODO()
также возвращает пустой контекст и используется в случаях, когда еще не ясно, какой контекст следует использовать. Это временный заполнитель, который можно заменить на другой контекст позже.ctx := context.TODO()
context.WithCancel(parent Context)
создает дочерний контекст, который может быть отменен явно вызовом функции отмены (cancel
). Это полезно для контроля выполнения горутин.ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go func() {
// Работа горутины
<-ctx.Done() // Ожидание отмены контекста
fmt.Println("Goroutine canceled")
}()
// Отмена контекста после некоторого времени
time.Sleep(2 * time.Second)
cancel()
context.WithDeadline(parent Context, d time.Time)
создает дочерний контекст, который будет автоматически отменен по истечении заданного времени (дедлайна).deadline := time.Now().Add(5 * time.Second)
ctx, cancel := context.WithDeadline(context.Background(), deadline)
defer cancel()
select {
case <-time.After(6 * time.Second):
fmt.Println("Done")
case <-ctx.Done():
fmt.Println("Context canceled:", ctx.Err())
}
context.WithTimeout(parent Context, timeout time.Duration)
создает дочерний контекст, который будет автоматически отменен через заданное время (таймаут). Это упрощенный вариант WithDeadline
.ctx, cancel := context.WithTimeout(context.Background(), 5 * time.Second)
defer cancel()
select {
case <-time.After(6 * time.Second):
fmt.Println("Done")
case <-ctx.Done():
fmt.Println("Context canceled:", ctx.Err())
}
context.WithValue(parent Context, key, val interface{})
создает дочерний контекст, который несет значение, связанное с заданным ключом. Это полезно для передачи метаданных между функциями.type key string
func main() {
ctx := context.WithValue(context.Background(), key("userID"), 12345)
process(ctx)
}
func process(ctx context.Context) {
if v := ctx.Value(key("userID")); v != nil {
fmt.Println("UserID:", v)
} else {
fmt.Println("UserID not found")
}
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5❤1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3👀1
Контекст (
context.Context
) используется для управления временем выполнения, обмена метаданными и отмены операций. Контексты задают таймауты и дедлайны, автоматически отменяя операции по истечении времени.
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://example.com", nil)
if err != nil {
fmt.Println("Error creating request:", err)
return
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
fmt.Println("Request failed:", err)
return
}
defer resp.Body.Close()
fmt.Println("Response status:", resp.Status)
Контексты позволяют явно отменять операции, что полезно для управления горутинами.
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go func(ctx context.Context) {
for {
select {
case <-ctx.Done():
fmt.Println("Goroutine canceled")
return
default:
fmt.Println("Goroutine working")
time.Sleep(500 * time.Millisecond)
}
}
}(ctx)
time.Sleep(2 * time.Second)
cancel()
time.Sleep(1 * time.Second)
Контексты позволяют передавать метаданные между функциями.
type key string
ctx := context.WithValue(context.Background(), key("userID"), 12345)
process(ctx)
func process(ctx context.Context) {
userID := ctx.Value(key("userID")).(int)
fmt.Println("UserID:", userID)
}
Контексты управляют жизненным циклом запросов, обеспечивая таймауты и отмену при завершении запросов.
func handler(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
fmt.Println("Handler started")
defer fmt.Println("Handler ended")
select {
case <-time.After(5 * time.Second):
fmt.Fprintln(w, "Request processed")
case <-ctx.Done():
err := ctx.Err()
fmt.Println("Handler error:", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
Контексты синхронизируют горутины и управляют их завершением.
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
worker(ctx, "Worker 1")
}()
go func() {
defer wg.Done()
worker(ctx, "Worker 2")
}()
wg.Wait()
func worker(ctx context.Context, name string) {
for {
select {
case <-ctx.Done():
fmt.Println(name, "stopped")
return
default:
fmt.Println(name, "working")
time.Sleep(1 * time.Second)
}
}
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3👍2
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3👀2
Микросервисы и монолиты — это два разных подхода к архитектуре программного обеспечения. Разница между ними заключается в способе организации кода и развертывания приложений.
Монолит — это приложение, в котором весь код собран в один единый блок. Все компоненты системы, включая пользовательский интерфейс, серверную часть, бизнес-логику и базу данных, интегрированы в один крупный исполняемый файл или модуль.
Микросервисы — это подход, при котором приложение разбивается на несколько независимых сервисов. Каждый микросервис отвечает за конкретную бизнес-функцию и взаимодействует с другими сервисами через хорошо определённые интерфейсы (обычно через API).
Представьте приложение для электронной коммерции, где интерфейс, управление пользователями, обработка заказов и управление продуктами — всё это часть одного большого приложения.
В приложении для электронной коммерции можно выделить отдельные микросервисы для управления пользователями, обработки заказов, управления продуктами и т.д. Эти микросервисы взаимодействуют друг с другом через API.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3😁1
Forwarded from Идущий к IT
Твое резюме на HeadHunter — ОК, если ты видишь это.
HeadHunter сравнивает ключевые навыки в твоем резюме и в вакансии и в момент отклика отображает, насколько % ты соответствуешь требованиям.
Специальный бейджик «Подходит по навыкам на 100%» отображается, если соответствие составляет более 60%.
Если при просмотре вакансий ты видишь такой бейджик, это значит, что список навыков в твоем резюме качественно составлен.
Это важный параметр, так как рекрутерам чаще показываются резюме с лучшим соответствием.
О том, как правильно указывать ключевые навыки и оптимизировать свое резюме я уже рассказывал в этом видео
HeadHunter сравнивает ключевые навыки в твоем резюме и в вакансии и в момент отклика отображает, насколько % ты соответствуешь требованиям.
Специальный бейджик «Подходит по навыкам на 100%» отображается, если соответствие составляет более 60%.
Если при просмотре вакансий ты видишь такой бейджик, это значит, что список навыков в твоем резюме качественно составлен.
Это важный параметр, так как рекрутерам чаще показываются резюме с лучшим соответствием.
О том, как правильно указывать ключевые навыки и оптимизировать свое резюме я уже рассказывал в этом видео
Мапы (карты) не являются потокобезопасными по умолчанию, что означает, что одновременное чтение и запись в мапу из нескольких горутин может привести к состояниям гонки, некорректным данным и паникам. Давайте рассмотрим, как можно обеспечить потокобезопасность при работе с мапами.
Если несколько горутин одновременно пытаются читать и изменять мапу, возникает конкуренция за доступ к данным, что приводит к непредсказуемому поведению программы. Это может включать утечки данных, несогласованность данных и даже крах программы.
Один из наиболее простых и распространенных способов обеспечить потокобезопасность — использование мьютексов (
sync.Mutex
). В этом примере мьютекс используется для защиты операций записи и чтения, обеспечивая эксклюзивный доступ к мапе.package main
import (
"fmt"
"sync"
)
func main() {
var mu sync.Mutex
m := make(map[string]int)
var wg sync.WaitGroup
write := func(key string, value int) {
mu.Lock()
m[key] = value
mu.Unlock()
}
read := func(key string) int {
mu.Lock()
defer mu.Unlock()
return m[key]
}
wg.Add(2)
go func() {
defer wg.Done()
write("key1", 42)
}()
go func() {
defer wg.Done()
fmt.Println(read("key1"))
}()
wg.Wait()
}
Если ожидается частое чтение и редкие записи, можно использовать
sync.RWMutex
, который позволяет множественное чтение, но блокирует доступ на запись. Здесь RWMutex
позволяет параллельное чтение, но блокирует все операции на запись, что улучшает производительность при частых чтениях.package main
import (
"fmt"
"sync"
)
func main() {
var mu sync.RWMutex
m := make(map[string]int)
var wg sync.WaitGroup
write := func(key string, value int) {
mu.Lock()
m[key] = value
mu.Unlock()
}
read := func(key string) int {
mu.RLock()
defer mu.RUnlock()
return m[key]
}
wg.Add(2)
go func() {
defer wg.Done()
write("key1", 42)
}()
go func() {
defer wg.Done()
fmt.Println(read("key1"))
}()
wg.Wait()
}
Go предоставляет пакет
sync.Map
, который изначально создан для конкурентного использования. sync.Map
автоматически обеспечивает потокобезопасность для операций чтения, записи и удаления.package main
import (
"fmt"
"sync"
)
func main() {
var m sync.Map
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
m.Store("key1", 42)
}()
go func() {
defer wg.Done()
value, _ := m.Load("key1")
fmt.Println(value)
}()
wg.Wait()
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥6
Проектировался с учетом простоты и производительности, и одно из решений разработчиков — не делать мапы (карты) потокобезопасными по умолчанию.
Потокобезопасность требует дополнительных накладных расходов для синхронизации доступа к данным. Если бы мапы в Go были потокобезопасными по умолчанию, каждый доступ к мапе был бы медленнее из-за необходимости блокировок. Большинство операций с мапами в реальных приложениях не требуют потокобезопасности, так как часто они выполняются в одном потоке или используются мапы, которые не модифицируются одновременно.
Go следует философии явного управления, где программисты сами должны решать, когда и как использовать синхронизацию. Это позволяет более точно контролировать производительность и обеспечивает гибкость при проектировании многопоточных приложений.
Потокобезопасные структуры данных, такие как
sync.Map
, могут быть сложными для понимания и использования. Введение потокобезопасности по умолчанию усложнило бы стандартные мапы, делая их менее интуитивными и простыми для использования.Эти мьютексы позволяют программистам вручную управлять синхронизацией доступа к мапам.
sync.Mutex
используется для блокировки доступа при чтении и записи, а sync.RWMutex
позволяет параллельное чтение и эксклюзивную запись.var mu sync.Mutex
m := make(map[string]int)
// запись с блокировкой
mu.Lock()
m["key"] = 42
mu.Unlock()
// чтение с блокировкой
mu.Lock()
value := m["key"]
mu.Unlock()
Специальная структура данных, которая изначально спроектирована для безопасного использования в многопоточной среде.
sync.Map
оптимизирован для сценариев с частыми чтениями и редкими записями.var m sync.Map
m.Store("key", 42)
value, ok := m.Load("key")
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4❤2
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
Каналы изначально проектировались как потокобезопасные средства для коммуникации между горутинами. Однако, несмотря на их встроенную потокобезопасность, есть определенные правила и ограничения, которые необходимо учитывать для корректного использования каналов.
Каналы безопасны для использования из нескольких горутин. Это означает, что вы можете отправлять данные в канал из одной горутины и получать данные из другой горутины без дополнительной синхронизации.
package main
import (
"fmt"
"sync"
)
func main() {
ch := make(chan int)
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
ch <- 42
}()
go func() {
defer wg.Done()
value := <-ch
fmt.Println(value)
}()
wg.Wait()
}
Несколько горутин могут безопасно отправлять данные в один и тот же канал.
package main
import (
"fmt"
"sync"
)
func main() {
ch := make(chan int, 2)
var wg sync.WaitGroup
wg.Add(3)
go func() {
defer wg.Done()
ch <- 1
}()
go func() {
defer wg.Done()
ch <- 2
}()
go func() {
defer wg.Done()
for i := 0; i < 2; i++ {
fmt.Println(<-ch)
}
}()
wg.Wait()
}
Несколько горутин могут безопасно получать данные из одного и того же канала.
package main
import (
"fmt"
"sync"
)
func main() {
ch := make(chan int, 2)
var wg sync.WaitGroup
ch <- 1
ch <- 2
wg.Add(2)
go func() {
defer wg.Done()
fmt.Println(<-ch)
}()
go func() {
defer wg.Done()
fmt.Println(<-ch)
}()
wg.Wait()
}
Канал должен закрываться только одной горутиной, и закрытие канала из нескольких горутин одновременно приведет к панике.
package main
func main() {
ch := make(chan int)
go func() {
close(ch) // Это допустимо
}()
go func() {
close(ch) // Это приведет к панике
}()
}
Попытка отправки данных в закрытый канал также вызовет панику. Поэтому, перед отправкой данных необходимо убедиться, что канал не закрыт.
package main
func main() {
ch := make(chan int)
close(ch)
ch <- 42 // Это приведет к панике
}
Если одна горутина закрывает канал, другая горутина может получить нулевое значение при чтении из него, что может привести к неправильным выводам, если это не предусмотрено.
package main
import (
"fmt"
)
func main() {
ch := make(chan int)
go func() {
close(ch)
}()
value, ok := <-ch
if !ok {
fmt.Println("Канал закрыт")
} else {
fmt.Println(value)
}
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Использование слайсов в многопоточном (concurrent) контексте в Go требует особого внимания к потокобезопасности. По умолчанию слайсы не являются потокобезопасными, что означает, что одновременное чтение и запись в слайс из нескольких горутин может привести к состояниям гонки, некорректным данным и паникам. Давайте рассмотрим, насколько безопасно использовать слайсы в условиях concurrency и какие подходы позволяют обеспечить безопасность.
Если несколько горутин одновременно изменяют содержимое слайса или его длину, это может привести к состояниям гонки и повреждению данных.
При добавлении элементов в слайс его размер может увеличиваться, что может привести к перераспределению памяти и копированию содержимого, создавая потенциальные проблемы при одновременном доступе.
Если слайс используется только для чтения, его можно безопасно использовать из нескольких горутин.
package main
import (
"fmt"
"sync"
)
func main() {
slice := []int{1, 2, 3, 4, 5}
var wg sync.WaitGroup
for _, v := range slice {
wg.Add(1)
go func(val int) {
defer wg.Done()
fmt.Println(val)
}(v)
}
wg.Wait()
}
Если каждая горутина должна иметь независимую копию слайса, передавайте копию в каждую горутину.
package main
import (
"fmt"
"sync"
)
func main() {
slice := []int{1, 2, 3, 4, 5}
var wg sync.WaitGroup
for _, v := range slice {
wg.Add(1)
go func(val int) {
defer wg.Done()
fmt.Println(val)
}(v)
}
wg.Wait()
}
Для обеспечения безопасного одновременного чтения и записи используйте механизмы синхронизации, такие как мьютексы.
package main
import (
"fmt"
"sync"
)
func main() {
slice := []int{1, 2, 3, 4, 5}
var wg sync.WaitGroup
for _, v := range slice {
wg.Add(1)
go func(val int) {
defer wg.Done()
fmt.Println(val)
}(v)
}
wg.Wait()
}
В некоторых случаях лучше использовать структуры данных, которые изначально проектированы для потокобезопасного доступа, такие как
sync.Map
для мап или специальные библиотеки для потокобезопасных коллекций.Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔1🤯1💊1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7🔥1
Нет, карты (maps) в Go не являются потокобезопасными по умолчанию. Использование карты в нескольких горутинах без должной синхронизации может привести к состояниям гонки, некорректным данным и паникам. Давайте рассмотрим более детально, почему это так и как можно обеспечить потокобезопасность при работе с картами в многопоточной среде.
Карты в Go разработаны для обеспечения высокой производительности при использовании в однопоточных сценариях. Если несколько горутин одновременно пытаются читать и изменять карту, возникает конкуренция за доступ к данным, что может привести к следующим проблемам:
Одновременный доступ к карте из нескольких горутин может привести к состояниям гонки, когда порядок выполнения операций непредсказуем.
Одновременное изменение структуры карты, например добавление или удаление элементов, может вызвать панику в программе.
Самый распространенный способ синхронизации доступа к карте — использование
sync.Mutex
. Мьютексы позволяют заблокировать доступ к карте на время чтения или записи.package main
import (
"fmt"
"sync"
)
func main() {
var mu sync.Mutex
m := make(map[string]int)
var wg sync.WaitGroup
write := func(key string, value int) {
mu.Lock()
m[key] = value
mu.Unlock()
}
read := func(key string) int {
mu.Lock()
defer mu.Unlock()
return m[key]
}
wg.Add(2)
go func() {
defer wg.Done()
write("key1", 42)
}()
go func() {
defer wg.Done()
fmt.Println(read("key1"))
}()
wg.Wait()
}
Если в вашей программе чаще происходят операции чтения, чем записи, можно использовать
sync.RWMutex
, который позволяет нескольким горутинам читать данные одновременно, но обеспечивает эксклюзивный доступ для записи.package main
import (
"fmt"
"sync"
)
func main() {
var mu sync.RWMutex
m := make(map[string]int)
var wg sync.WaitGroup
write := func(key string, value int) {
mu.Lock()
m[key] = value
mu.Unlock()
}
read := func(key string) int {
mu.RLock()
defer mu.RUnlock()
return m[key]
}
wg.Add(2)
go func() {
defer wg.Done()
write("key1", 42)
}()
go func() {
defer wg.Done()
fmt.Println(read("key1"))
}()
wg.Wait()
}
Go предоставляет специальную структуру
sync.Map
, которая изначально создана для безопасного использования в многопоточной среде. Она автоматически обеспечивает синхронизацию операций.package main
import (
"fmt"
"sync"
)
func main() {
var m sync.Map
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
m.Store("key1", 42)
}()
go func() {
defer wg.Done()
value, _ := m.Load("key1")
fmt.Println(value)
}()
wg.Wait()
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4❤1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
Каналы в Go являются потокобезопасными, но это не означает, что они полностью освобождены от правил и ограничений при работе в многопоточной среде. Давайте разберем, насколько безопасны каналы и какие практики нужно соблюдать при их использовании.
Каналы в Go предназначены для безопасного обмена данными между горутинами. Они обеспечивают синхронизацию отправки и получения данных, что делает их безопасными для использования в многопоточной среде. Однако существуют важные моменты, которые необходимо учитывать:
Каналы потокобезопасны для операций отправки и получения. Это означает, что несколько горутин могут безопасно отправлять данные в канал и получать данные из канала одновременно.
package main
import (
"fmt"
"sync"
)
func main() {
ch := make(chan int)
var wg sync.WaitGroup
// Горутина для отправки данных
wg.Add(1)
go func() {
defer wg.Done()
ch <- 42
}()
// Горутина для получения данных
wg.Add(1)
go func() {
defer wg.Done()
value := <-ch
fmt.Println(value)
}()
wg.Wait()
}
Закрытие канала должно выполняться только одной горутиной. Попытка закрытия канала из нескольких горутин может привести к панике.
package main
func main() {
ch := make(chan int)
// Правильный способ закрытия канала
go func() {
close(ch)
}()
// Неправильный способ (может вызвать панику)
// go func() {
// close(ch)
// }()
}
Отправка данных в закрытый канал вызывает панику, поэтому необходимо быть уверенным, что канал не закрыт перед отправкой данных.
package main
func main() {
ch := make(chan int)
close(ch)
// Попытка отправки данных в закрытый канал вызовет панику
// ch <- 42
}
Чтение из закрытого канала безопасно. При чтении из закрытого канала, если канал пуст, будет возвращено нулевое значение типа и флаг, указывающий на закрытие канала.
package main
import "fmt"
func main() {
ch := make(chan int)
close(ch)
value, ok := <-ch
fmt.Println(value, ok) // Вывод: 0 false
}
Буферизованные каналы позволяют отправлять несколько значений без блокировки, пока буфер не заполнится, что может улучшить производительность.
package main
import "fmt"
func main() {
ch := make(chan int, 2)
ch <- 1
ch <- 2
fmt.Println(<-ch) // 1
fmt.Println(<-ch) // 2
}
Не используйте каналы, если простое использование мьютексов или других средств синхронизации подходит лучше. Каналы предназначены для передачи данных между горутинами, а не для синхронизации доступа к общей памяти.
Если канал должен быть закрыт, делайте это только после того, как все отправляющие горутины завершат свою работу.
package main
import "fmt"
func main() {
ch := make(chan int)
go func() {
for i := 0; i < 5; i++ {
ch <- i
}
close(ch)
}()
for value := range ch {
fmt.Println(value)
}
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3🔥3