В Go конструкция
select
используется для обработки операций с каналами. Она позволяет горутинам ждать событий на нескольких каналах одновременно. Аналогом конструкции default
в select
является опция, позволяющая избежать блокировки, если ни один из каналов не готов.Когда
select
выполняется, он блокируется до тех пор, пока один из его case-блоков не станет готов к выполнению (т.е. канал не станет доступным для отправки или получения данных). Однако, если добавить блок default
, select
может выполнить его немедленно, если ни один из других case-блоков не готов. Это позволяет избежать блокировки горутины.Мы используем
select
с default
, чтобы выполнить неблокирующие операции с каналами.package main
import (
"fmt"
"time"
)
func main() {
ch1 := make(chan int)
ch2 := make(chan int)
go func() {
time.Sleep(2 * time.Second)
ch1 <- 1
}()
go func() {
time.Sleep(1 * time.Second)
ch2 <- 2
}()
for i := 0; i < 3; i++ {
select {
case val := <-ch1:
fmt.Println("Received from ch1:", val)
case val := <-ch2:
fmt.Println("Received from ch2:", val)
default:
fmt.Println("No channel is ready")
time.Sleep(500 * time.Millisecond)
}
}
}
ch1
и ch2
.select
с default
, который проверяет готовность каналов каждые 500 миллисекунд.default
, выводящий сообщение "No channel is ready".Если вы хотите проверить наличие данных на канале или возможность отправки данных без блокировки, используйте
default
.В некоторых случаях
default
может предотвратить дедлоки, позволяя горутине продолжить выполнение, даже если каналы временно недоступны.Можно использовать
default
для периодической проверки состояния канала или выполнения задач в ожидании готовности каналов.Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
В Go
sync.WaitGroup
используется для синхронизации выполнения горутин. Она позволяет основной горутине (или любой другой горутине) ждать завершения группы горутин перед продолжением работы. Это особенно полезно, когда нужно убедиться, что все фоновые задачи завершены до выполнения дальнейших действий.Увеличивает (или уменьшает) счетчик горутин на заданное значение
delta
.Обычно вызывается до запуска горутин, чтобы установить количество горутин, которые нужно дождаться.
Уменьшает счетчик горутин на 1.
Вызывается горутиной, когда она завершает свою работу.
Блокирует выполнение до тех пор, пока счетчик горутин не станет равен нулю.
Обычно вызывается основной горутиной для ожидания завершения всех горутин.
Мы используем
WaitGroup
для ожидания завершения нескольких горутин.package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done() // Уменьшает счетчик на 1 при завершении работы горутины
fmt.Printf("Worker %d starting\n", id)
time.Sleep(time.Second)
fmt.Printf("Worker %d done\n", id)
}
func main() {
var wg sync.WaitGroup
for i := 1; i <= 5; i++ {
wg.Add(1) // Увеличивает счетчик горутин на 1
go worker(i, &wg)
}
wg.Wait() // Ожидает завершения всех горутин
fmt.Println("All workers done")
}
worker
.WaitGroup
увеличивается на 1 перед запуском каждой горутины с помощью wg.Add(1)
.wg.Done()
при завершении, уменьшая счетчик на 1.wg.Wait()
, блокируясь до тех пор, пока все горутины не завершат свою работу.Позволяет основной горутине дождаться завершения всех запущенных горутин, что особенно важно для корректного завершения программы или выполнения зависимых задач.
Гарантирует, что основная горутина не завершит выполнение программы до того, как завершатся все горутины, предотвращая возможные дедлоки или незавершенные операции.
Позволяет легко управлять множеством горутин, не требуя сложной логики для отслеживания их завершения.
Без использования
WaitGroup
основной поток может завершиться до завершения всех горутин, что приведет к неполной обработке данных. В этом примере использование time.Sleep
для ожидания является ненадежным и не гарантирует завершение всех горутин. Вместо этого правильное использование WaitGroup
обеспечивает корректное завершение всех задач.package main
import (
"fmt"
"time"
)
func worker(id int) {
fmt.Printf("Worker %d starting\n", id)
time.Sleep(time.Second)
fmt.Printf("Worker %d done\n", id)
}
func main() {
for i := 1; i <= 5; i++ {
go worker(i)
}
time.Sleep(2 * time.Second) // Это не гарантирует завершение всех горутин
fmt.Println("All workers done")
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
sync.Map
в Go — это потокобезопасная структура данных, предназначенная для конкурентного доступа к ключам и значениям, которая была введена в Go 1.9. Она предоставляет удобные методы для работы с картой в многопоточной среде без необходимости вручную управлять блокировками.sync.Map
изначально спроектирована для безопасного использования в многопоточной среде. Все операции по чтению, записи и удалению синхронизированы.sync.Map
предоставляет высокоуровневые методы для работы с картой, что упрощает её использование в сравнении с традиционными картами и мьютексами.Сохраняет значение по заданному ключу. Если ключ уже существует, его значение будет перезаписано.
var m sync.Map
m.Store("foo", 1)
Возвращает значение, сохраненное по заданному ключу, и булев флаг, указывающий, было ли найдено значение.
value, ok := m.Load("foo")
if ok {
fmt.Println("Found:", value)
} else {
fmt.Println("Not found")
}
Возвращает существующее значение по ключу, если ключ уже существует, иначе сохраняет и возвращает новое значение. Булев флаг указывает, существовал ли ключ до вызова.
actual, loaded := m.LoadOrStore("foo", 1)
if loaded {
fmt.Println("Existing value:", actual)
} else {
fmt.Println("Stored new value:", actual)
}
Удаляет значение по заданному ключу.
m.Delete("foo")
Выполняет функцию для каждого ключа и значения в карте. Если функция возвращает
false
, обход прекращается.m.Store("foo", 1)
m.Store("bar", 2)
m.Range(func(key, value interface{}) bool {
fmt.Println(key, value)
return true // продолжить обход
})
Несколько горутин безопасно работают с
sync.Map
.package main
import (
"fmt"
"sync"
)
func main() {
var m sync.Map
var wg sync.WaitGroup
// Запись в sync.Map из нескольких горутин
for i := 0; i < 5; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
m.Store(i, i*i)
}(i)
}
wg.Wait()
// Чтение из sync.Map
m.Range(func(key, value interface{}) bool {
fmt.Printf("key: %v, value: %v\n", key, value)
return true
})
}
sync.Map
оптимизирована для сценариев с частыми операциями чтения и редкими операциями записи. Она может быть менее эффективной при частых изменениях структуры карты.Если нужно использовать карту в многопоточной среде и не хочется вручную управлять блокировками с помощью мьютексов.
sync.Map
может сделать код более понятным и простым за счет использования высокоуровневых методов вместо ручного управления блокировками.Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Объединение карты (map) и мьютексов (mutexes) в Go — это стандартный способ обеспечения потокобезопасности при доступе к данным в многопоточной среде. Это позволяет использовать карту для хранения данных и мьютексы для синхронизации доступа к этим данным.
Мы создадим структуру, которая будет содержать карту и мьютекс. Это позволит нам инкапсулировать доступ к карте и обеспечить безопасный доступ через методы этой структуры.
Реализуем методы для безопасного чтения, записи и удаления элементов карты, используя мьютекс для синхронизации.
Мы создадим потокобезопасную карту с использованием мьютекса.
package main
import (
"fmt"
"sync"
)
// SafeMap структура, содержащая карту и мьютекс для синхронизации
type SafeMap struct {
mu sync.Mutex
m map[string]int
}
// NewSafeMap создает новый SafeMap
func NewSafeMap() *SafeMap {
return &SafeMap{
m: make(map[string]int),
}
}
// Store сохраняет значение в карту
func (sm *SafeMap) Store(key string, value int) {
sm.mu.Lock()
defer sm.mu.Unlock()
sm.m[key] = value
}
// Load возвращает значение из карты
func (sm *SafeMap) Load(key string) (int, bool) {
sm.mu.Lock()
defer sm.mu.Unlock()
value, ok := sm.m[key]
return value, ok
}
// Delete удаляет значение из карты
func (sm *SafeMap) Delete(key string) {
sm.mu.Lock()
defer sm.mu.Unlock()
delete(sm.m, key)
}
// Range выполняет функцию для каждого ключа и значения в карте
func (sm *SafeMap) Range(f func(key string, value int) bool) {
sm.mu.Lock()
defer sm.mu.Unlock()
for k, v := range sm.m {
if !f(k, v) {
break
}
}
}
func main() {
safeMap := NewSafeMap()
var wg sync.WaitGroup
// Запись в SafeMap из нескольких горутин
for i := 0; i < 5; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
key := fmt.Sprintf("key%d", i)
safeMap.Store(key, i*i)
}(i)
}
wg.Wait()
// Чтение из SafeMap
safeMap.Range(func(key string, value int) bool {
fmt.Printf("key: %s, value: %d\n", key, value)
return true
})
}
Структура
SafeMap
содержит мьютекс mu
и карту m
, что позволяет синхронизировать доступ к карте.Каждый метод сначала блокирует мьютекс
mu
для обеспечения эксклюзивного доступа к карте, а затем выполняет соответствующую операцию (сохранение, загрузка, удаление, обход). Метод Range
позволяет выполнить функцию для каждого ключа и значения в карте, обеспечивая синхронизацию на время обхода.Мьютекс обеспечивает синхронизацию доступа, предотвращая состояния гонки и обеспечивая корректное чтение и запись данных.
Инкапсулируя карту и мьютекс в структуру, мы упрощаем код и делаем его более чистым и понятным. Пользователи структуры не нуждаются в управлении блокировками вручную.
Методы структуры обеспечивают простой и удобный интерфейс для работы с потокобезопасной картой.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
RWMutex (Read-Write Mutex) позволяет разделять доступ для чтения и блокировать только для записи, в отличие от обычного Mutex, который блокирует как чтение, так и запись. С помощью RWMutex можно использовать RLock для параллельного доступа к данным в режиме чтения, что увеличивает производительность, если много горутин читают данные и редко их изменяют. Для записи используется Lock, который блокирует доступ к данным для всех других операций чтения и записи.
sync.Mutex
используется для обеспечения эксклюзивного доступа к ресурсу для всех операций (чтение и запись). Подходит для сценариев с частыми операциями записи или равномерным распределением чтений и записей.var mu sync.Mutex
var counter int
func increment() {
mu.Lock()
counter++
mu.Unlock()
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
increment()
}()
}
wg.Wait()
fmt.Println("Counter:", counter)
}
sync.RWMutex
предоставляет два типа блокировок: для чтения (RLock
) и для записи (Lock
). Позволяет нескольким горутинам одновременно читать данные, если нет активных блокировок для записи. Улучшает производительность в сценариях с частыми чтениями и редкими записями.var rwMu sync.RWMutex
var counter int
func readCounter() int {
rwMu.RLock()
defer rwMu.RUnlock()
return counter
}
func increment() {
rwMu.Lock()
counter++
rwMu.Unlock()
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func() {
defer wg.Done()
fmt.Println("Read Counter:", readCounter())
}()
}
for i := 0; i < 5; i++ {
wg.Add(1)
go func() {
defer wg.Done()
increment()
}()
}
wg.Wait()
fmt.Println("Final Counter:", counter)
}
sync.Mutex
блокирует доступ для всех операций.sync.RWMutex
позволяет одновременное чтение, но блокирует доступ при записи.sync.Mutex
подходит для частых записей или равномерного распределения операций.sync.RWMutex
подходит для частых чтений и редких записей.Используйте
sync.Mutex
для эксклюзивного доступа без различий между чтением и записью.Используйте
sync.RWMutex
для улучшения производительности при частых чтениях.Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Это состояние, при котором одна или несколько горутин (или потоков) не получают достаточного доступа к необходимым ресурсам для выполнения своих задач. Это может произойти по нескольким причинам, таким как неправильное управление блокировками или приоритетами, что приводит к несправедливому распределению ресурсов.
Если потоки с более низким приоритетом постоянно вытесняются потоками с более высоким приоритетом, они могут вообще не получить доступ к ресурсу.
При использовании мьютексов или других механизмов синхронизации, если один поток постоянно захватывает мьютекс, другие потоки могут не получить возможности захватить его и выполнить свою работу.
Потоки могут попасть в состояние бесконечного ожидания, если они не могут получить доступ к ресурсу из-за постоянной занятости другими потоками.
Один поток постоянно захватывает мьютекс, не давая другим потокам шанса выполнить свою работу. В этом примере функция
greedyWorker
постоянно захватывает мьютекс, блокируя доступ к sharedResource
для других горутин (функций starvedWorker
). Это приводит к тому, что starvedWorker
не получает шанса выполнить свою работу.package main
import (
"fmt"
"sync"
"time"
)
var mu sync.Mutex
var sharedResource int
func greedyWorker() {
for {
mu.Lock()
sharedResource++
fmt.Println("Greedy worker incremented sharedResource to:", sharedResource)
mu.Unlock()
time.Sleep(100 * time.Millisecond) // Короткая задержка для демонстрации
}
}
func starvedWorker(id int, wg *sync.WaitGroup) {
defer wg.Done()
mu.Lock()
fmt.Printf("Starved worker %d is running\n", id)
sharedResource++
mu.Unlock()
}
func main() {
var wg sync.WaitGroup
wg.Add(5)
go greedyWorker()
for i := 1; i <= 5; i++ {
go starvedWorker(i, &wg)
}
wg.Wait()
fmt.Println("Final sharedResource value:", sharedResource)
}
Используйте справедливые алгоритмы блокировки, которые предоставляют доступ к ресурсу в порядке очереди.
Обеспечьте сбалансированное управление приоритетами потоков, чтобы потоки с низким приоритетом также получали доступ к ресурсам.
Например, вместо использования мьютексов, можно использовать каналы в Go для координации работы между горутинами.
Ограничьте время, в течение которого поток может удерживать мьютекс, чтобы другие потоки также могли получить доступ.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Для обеспечения потокобезопасности мапы в Go обычно используется комбинация мьютексов (
sync.Mutex
) или RW-мьютексов (sync.RWMutex
). Давайте рассмотрим, как каждый из них применяется для защиты мапы.sync.Mutex
обеспечивает эксклюзивный доступ к мапе для всех операций (чтение и запись). Это простой и эффективный способ синхронизации, но при частых операциях чтения может возникнуть узкое место, так как блокируются все операции.package main
import (
"fmt"
"sync"
)
type SafeMap struct {
mu sync.Mutex
m map[string]int
}
func NewSafeMap() *SafeMap {
return &SafeMap{
m: make(map[string]int),
}
}
func (sm *SafeMap) Store(key string, value int) {
sm.mu.Lock()
defer sm.mu.Unlock()
sm.m[key] = value
}
func (sm *SafeMap) Load(key string) (int, bool) {
sm.mu.Lock()
defer sm.mu.Unlock()
value, ok := sm.m[key]
return value, ok
}
func (sm *SafeMap) Delete(key string) {
sm.mu.Lock()
defer sm.mu.Unlock()
delete(sm.m, key)
}
func main() {
sm := NewSafeMap()
sm.Store("foo", 1)
value, ok := sm.Load("foo")
if ok {
fmt.Println("Found:", value)
} else {
fmt.Println("Not found")
}
}
sync.RWMutex
предоставляет два типа блокировок: для чтения (RLock
) и для записи (Lock
). Он позволяет нескольким горутинам одновременно читать данные, если нет активных блокировок для записи. Это улучшает производительность в сценариях с частыми операциями чтения и редкими операциями записи.package main
import (
"fmt"
"sync"
)
type SafeMap struct {
mu sync.RWMutex
m map[string]int
}
func NewSafeMap() *SafeMap {
return &SafeMap{
m: make(map[string]int),
}
}
func (sm *SafeMap) Store(key string, value int) {
sm.mu.Lock()
defer sm.mu.Unlock()
sm.m[key] = value
}
func (sm *SafeMap) Load(key string) (int, bool) {
sm.mu.RLock()
defer sm.mu.RUnlock()
value, ok := sm.m[key]
return value, ok
}
func (sm *SafeMap) Delete(key string) {
sm.mu.Lock()
defer sm.mu.Unlock()
delete(sm.m, key)
}
func main() {
sm := NewSafeMap()
sm.Store("foo", 1)
value, ok := sm.Load("foo")
if ok {
fmt.Println("Found:", value)
} else {
fmt.Println("Not found")
}
}
Используйте, если операции чтения и записи происходят с примерно одинаковой частотой. Подходит для простых сценариев, где производительность не критична.
Используйте, если чтения происходят значительно чаще, чем записи. Улучшает производительность за счет одновременного выполнения нескольких операций чтения.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Конструкция
select
используется для обработки операций с каналами. Она позволяет горутинам ожидать на нескольких каналах одновременно. Важной особенностью select
является возможность использования блока default
, который выполняется, если ни один из каналов не готов к операции (отправке или получению данных). Это позволяет избежать блокировки горутины, если каналы не готовы.Когда
select
выполняется, он блокируется до тех пор, пока один из его case-блоков не станет готов к выполнению (т.е. канал не станет доступным для отправки или получения данных). Однако, если добавить блок default
, select
выполнит его немедленно, если ни один из других case-блоков не готов. Это позволяет избежать блокировки горутины и продолжить выполнение других задач.package main
import (
"fmt"
"time"
)
func main() {
ch1 := make(chan int)
ch2 := make(chan int)
go func() {
time.Sleep(2 * time.Second)
ch1 <- 1
}()
go func() {
time.Sleep(1 * time.Second)
ch2 <- 2
}()
for i := 0; i < 5; i++ {
select {
case val := <-ch1:
fmt.Println("Received from ch1:", val)
case val := <-ch2:
fmt.Println("Received from ch2:", val)
default:
fmt.Println("No channel is ready")
time.Sleep(500 * time.Millisecond)
}
}
}
ch1
и ch2
.select
с default
, который проверяет готовность каналов каждые 500 миллисекунд.default
, выводящий сообщение "No channel is ready".Если вы хотите проверить наличие данных на канале или возможность отправки данных без блокировки, используйте
default
.В некоторых случаях
default
может предотвратить дедлоки, позволяя горутине продолжить выполнение, даже если каналы временно недоступны.Можно использовать
default
для периодической проверки состояния канала или выполнения задач в ожидании готовности каналов.Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Мапы (maps) не являются потокобезопасными по умолчанию. Если несколько горутин одновременно пытаются записывать (или читать и записывать) в одну и ту же мапу, это может привести к непредсказуемым результатам, состояниям гонки и даже краху программы (panic).
Вот пример, который иллюстрирует проблему конкурентной записи в мапу. В этом примере множество горутин одновременно записывают значения в мапу
m
, что приведет к состоянию гонки и, возможно, к панике.package main
import (
"fmt"
"sync"
)
func main() {
m := make(map[int]int)
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
m[i] = i
}(i)
}
wg.Wait()
fmt.Println("Done")
}
Позволяют синхронизировать доступ к мапе, обеспечивая эксклюзивный доступ для записи и чтения.
package main
import (
"fmt"
"sync"
)
type SafeMap struct {
mu sync.Mutex
m map[int]int
}
func NewSafeMap() *SafeMap {
return &SafeMap{
m: make(map[int]int),
}
}
func (sm *SafeMap) Store(key, value int) {
sm.mu.Lock()
defer sm.mu.Unlock()
sm.m[key] = value
}
func (sm *SafeMap) Load(key int) (int, bool) {
sm.mu.Lock()
defer sm.mu.Unlock()
value, ok := sm.m[key]
return value, ok
}
func main() {
sm := NewSafeMap()
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
sm.Store(i, i)
}(i)
}
wg.Wait()
if value, ok := sm.Load(42); ok {
fmt.Println("Found:", value)
} else {
fmt.Println("Not found")
}
}
Это потокобезопасная структура данных, специально созданная для конкурентного использования. Она предоставляет высокоуровневые методы для безопасной работы с мапой без необходимости управления мьютексами вручную.
package main
import (
"fmt"
"sync"
)
func main() {
var m sync.Map
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
m.Store(i, i)
}(i)
}
wg.Wait()
if value, ok := m.Load(42); ok {
fmt.Println("Found:", value)
} else {
fmt.Println("Not found")
}
}
Позволяет более точный контроль над доступом к мапе. Могут быть использованы для различных структур данных, не только для мап.
Упрощает код, устраняя необходимость в ручной синхронизации. Оптимизирован для сценариев с частыми чтениями и редкими записями.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
sync.Mutex
обеспечивает эксклюзивный доступ для чтения и записи.sync.RWMutex
позволяет одновременное чтение несколькими горутинами и эксклюзивный доступ для записи.var mu sync.Mutex
var rwMu sync.RWMutex
func increment() {
mu.Lock()
counter++
mu.Unlock()
}
func readCounter() int {
rwMu.RLock()
defer rwMu.RUnlock()
return counter
}
Синхронизируют горутины через передачу данных. Буферизованные каналы могут хранить несколько значений, небуферизованные требуют синхронной отправки и получения.
ch := make(chan int, 1)
go func() { ch <- 42 }()
fmt.Println(<-ch)
Позволяют основной горутине ждать завершения всех запущенных горутин.
var wg sync.WaitGroup
wg.Add(1)
go func() { defer wg.Done(); /* работа */ }()
wg.Wait()
Потокобезопасная карта для конкурентного доступа.
var m sync.Map
m.Store("key", "value")
value, ok := m.Load("key")
Гарантирует выполнение кода только один раз.
var once sync.Once
once.Do(func() { /* инициализация */ })
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Предоставляют механизм для безопасного выполнения низкоуровневых операций чтения и записи данных без использования блокировок, таких как мьютексы. Эти операции гарантируют, что изменения данных будут завершены полностью и последовательно, что позволяет избежать состояний гонки.
atomic.LoadInt32
, atomic.LoadInt64
, atomic.LoadUint32
, atomic.LoadUint64
, atomic.LoadUintptr
atomic.StoreInt32
, atomic.StoreInt64
, atomic.StoreUint32
, atomic.StoreUint64
, atomic.StoreUintptr
var x int32
atomic.StoreInt32(&x, 42)
value := atomic.LoadInt32(&x)
atomic.AddInt32
, atomic.AddInt64
, atomic.AddUint32
, atomic.AddUint64
var x int32
atomic.AddInt32(&x, 1)
atomic.CompareAndSwapInt32
, atomic.CompareAndSwapInt64
, atomic.CompareAndSwapUint32
, atomic.CompareAndSwapUint64
var x int32 = 42
swapped := atomic.CompareAndSwapInt32(&x, 42, 100)
atomic.SwapInt32
, atomic.SwapInt64
, atomic.SwapUint32
, atomic.SwapUint64
var x int32
old := atomic.SwapInt32(&x, 100)
atomic.LoadPointer
, atomic.StorePointer
, atomic.SwapPointer
, atomic.CompareAndSwapPointer
var ptr unsafe.Pointer
atomic.StorePointer(&ptr, unsafe.Pointer(&x))
p := atomic.LoadPointer(&ptr)
Атомарные операции могут быть быстрее, чем использование мьютексов, так как они выполняются без блокировок. Их следует использовать, когда требуется минимальная задержка и высокая производительность.
Атомарные операции идеальны для реализации простых счетчиков, флагов или индикаторов состояния.
Когда требуется низкоуровневый контроль над синхронизацией данных без затрат на мьютексы.
package main
import (
"fmt"
"sync"
"sync/atomic"
)
func main() {
var counter int32
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
defer wg.Done()
atomic.AddInt32(&counter, 1)
}()
}
wg.Wait()
fmt.Println("Final Counter:", counter)
}
Атомарные операции не подходят для более сложных операций, требующих нескольких шагов, которые должны быть атомарными. В таких случаях лучше использовать мьютексы.
Атомарные операции работают с простыми типами данных (числа, указатели). Для сложных структур данных лучше использовать другие механизмы синхронизации.
Код, использующий атомарные операции, может быть сложнее для понимания и поддержки, особенно если требуются сложные логические операции.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM