Co jsou atomové operace, jaké existují druhy a kdy a jak je nejlépe používat?
sobes.tech AI
Odpověď od AI
Atomové operace jsou operace nad pamětí, jejichž provedení je garantováno buď úplně, nebo vůbec nezačne, a nemůže být přerušeno jinými konkurenčními operacemi. Jsou nedělitelné a hardwarově bezpečné.
V jazyce Go balíček sync/atomic poskytuje sadu funkcí pro provádění atomových operací:
| Funkce | Popis |
|---|---|
AddInt32 |
Atomicky přičítá hodnotu k int32. |
AddInt64 |
Atomicky přičítá hodnotu k int64. |
AddUint32 |
Atomicky přičítá hodnotu k uint32. |
AddUint64 |
Atomicky přičítá hodnotu k uint64. |
AddUintptr |
Atomicky přičítá hodnotu k uintptr. |
CompareAndSwapInt32 |
Atomicky porovnává a mění (CAS) int32. |
CompareAndSwapInt64 |
Atomicky porovnává a mění (CAS) int64. |
CompareAndSwapUint32 |
Atomicky porovnává a mění (CAS) uint32. |
CompareAndSwapUint64 |
Atomicky porovnává a mění (CAS) uint64. |
CompareAndSwapUintptr |
Atomicky porovnává a mění (CAS) uintptr. |
LoadInt32 |
Atomicky načítá hodnotu int32. |
LoadInt64 |
Atomicky načítá hodnotu int64. |
LoadUint32 |
Atomicky načítá hodnotu uint32. |
LoadUint64 |
Atomicky načítá hodnotu uint64. |
LoadUintptr |
Atomicky načítá hodnotu uintptr. |
LoadPointer |
Atomicky načítá hodnotu unsafe.Pointer. |
StoreInt32 |
Atomicky ukládá hodnotu int32. |
StoreInt64 |
Atomicky ukládá hodnotu int64. |
StoreUint32 |
Atomicky ukládá hodnotu uint32. |
StoreUint64 |
Atomicky ukládá hodnotu uint64. |
StoreUintptr |
Atomicky ukládá hodnotu uintptr. |
StorePointer |
Atomicky ukládá hodnotu unsafe.Pointer. |
SwapInt32 |
Atomicky zaměňuje hodnotu int32. |
SwapInt64 |
Atomicky zaměňuje hodnotu int64. |
SwapUint32 |
Atomicky zaměňuje hodnotu uint32. |
SwapUint64 |
Atomicky zaměňuje hodnotu uint64. |
SwapUintptr |
Atomicky zaměňuje hodnotu uintptr. |
SwapPointer |
Atomicky zaměňuje hodnotu unsafe.Pointer. |
Použití atomových operací je nejvhodnější v případech, kdy je potřeba jednoduchá, nízkoúrovňová synchronizace přístupu ke sdíleným proměnným, nejčastěji čítačům nebo flagům, bez použití mutexů. Zajišťují minimální režii a lepší výkon v takových scénářích.
Příklad použití atomického čítače:
package main
import (
"fmt"
"sync"
"sync/atomic"
)
func main() {
var counter int64
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
// Atomicky zvyšujeme čítač
atomic.AddInt64(&counter, 1)
wg.Done()
}()
}
wg.Wait()
// Atomicky načítáme hodnotu čítače
fmt.Println("Final Counter:", atomic.LoadInt64(&counter))
}
Příklad použití CompareAndSwap:
package main
import (
"fmt"
"sync/atomic"
)
func main() {
var value int32 = 10
// Pokusit se změnit hodnotu, pokud je aktuální 10, na 20
swapped := atomic.CompareAndSwapInt32(&value, 10, 20)
fmt.Println("Swapped:", swapped) // Swapped: true
fmt.Println("Value:", value) // Value: 20
// Pokusit se změnit hodnotu, pokud je aktuální 10, na 30
swapped = atomic.CompareAndSwapInt32(&value, 10, 30)
fmt.Println("Swapped:", swapped) // Swapped: false
fmt.Println("Value:", value) // Value: 20
}
Je důležité si uvědomit, že atomické operace pracují pouze s primitivními typy (celá čísla, ukazatele). Pro synchronizaci přístupu ke složitějším datovým strukturám nebo provádění blokujících operací jsou stále potřeba mutexy nebo jiné mechanismy synchronizace. Nadměrné nebo nesprávné používání atomických operací může vést k těžko odhalitelným chybám nebo ke snížení výkonu.