Sobes.tech
Middle+

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.