Sobes.tech
Middle+

Was sind atomare Operationen, welche Arten gibt es und wann und wie sollte man sie am besten verwenden?

sobes.tech KI

Antwort von AI

Atomare Operationen sind Operationen im Speicher, deren Ausführung garantiert, dass sie entweder vollständig abgeschlossen werden oder gar nicht beginnen, und sie können nicht durch andere konkurrierende Operationen unterbrochen werden. Sie sind unteilbar und auf Hardware-Ebene thread-sicher.

In Go bietet das Paket sync/atomic eine Reihe von Funktionen für atomare Operationen:

Funktion Beschreibung
AddInt32 Fügt atomar einen Wert zu int32 hinzu.
AddInt64 Fügt atomar einen Wert zu int64 hinzu.
AddUint32 Fügt atomar einen Wert zu uint32 hinzu.
AddUint64 Fügt atomar einen Wert zu uint64 hinzu.
AddUintptr Fügt atomar einen Wert zu uintptr hinzu.
CompareAndSwapInt32 Vergleicht und tauscht (CAS) int32 atomar.
CompareAndSwapInt64 Vergleicht und tauscht (CAS) int64 atomar.
CompareAndSwapUint32 Vergleicht und tauscht (CAS) uint32.
CompareAndSwapUint64 Vergleicht und tauscht (CAS) uint64.
CompareAndSwapUintptr Vergleicht und tauscht (CAS) uintptr.
CompareAndSwapPointer Vergleicht und tauscht (CAS) unsafe.Pointer.
LoadInt32 Lädt atomar den Wert von int32.
LoadInt64 Lädt atomar den Wert von int64.
LoadUint32 Lädt atomar den Wert von uint32.
LoadUint64 Lädt atomar den Wert von uint64.
LoadUintptr Lädt atomar den Wert von uintptr.
LoadPointer Lädt atomar den Wert von unsafe.Pointer.
StoreInt32 Speichert atomar den Wert von int32.
StoreInt64 Speichert atomar den Wert von int64.
StoreUint32 Speichert atomar den Wert von uint32.
StoreUint64 Speichert atomar den Wert von uint64.
StoreUintptr Speichert atomar den Wert von uintptr.
StorePointer Speichert atomar den Wert von unsafe.Pointer.
SwapInt32 Tauscht atomar die Werte von int32.
SwapInt64 Tauscht atomar die Werte von int64.
SwapUint32 Tauscht atomar die Werte von uint32.
SwapUint64 Tauscht atomar die Werte von uint64.
SwapUintptr Tauscht atomar die Werte von uintptr.
SwapPointer Tauscht atomar die Werte von unsafe.Pointer.

Am besten verwendet man atomare Operationen in Fällen, in denen eine einfache, low-level Synchronisation für den Zugriff auf gemeinsame Variablen erforderlich ist, meist Zähler oder Flags, ohne Mutexes zu verwenden. Sie bieten minimale Overheads und bessere Leistung in solchen Szenarien.

Beispiel für die Verwendung eines atomaren Zählers:

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() {
			// Atomare Erhöhung des Zählers
			atomic.AddInt64(&counter, 1)
			wg.Done()
		}()
	}

	wg.Wait()
	// Atomare Ladung des Zählerwerts
	fmt.Println("Endzähler:", atomic.LoadInt64(&counter))
}

Beispiel für CompareAndSwap:

package main

import (
	"fmt"
	"sync/atomic"
)

func main() {
	var value int32 = 10

	// Versuch, den Wert zu tauschen, wenn er aktuell 10 ist, auf 20
	swapped := atomic.CompareAndSwapInt32(&value, 10, 20)
	fmt.Println("Tausch:", swapped) // true
	fmt.Println("Wert:", value)   // 20

	// Versuch, den Wert zu tauschen, wenn er aktuell 10 ist, auf 30
	swapped = atomic.CompareAndSwapInt32(&value, 10, 30)
	fmt.Println("Tausch:", swapped) // false
	fmt.Println("Wert:", value)   // 20
}

Wichtig ist, dass atomare Operationen nur mit primitiven Datentypen (Ganzzahlen, Zeiger) funktionieren. Für die Synchronisation komplexerer Datenstrukturen oder blockierende Operationen sind weiterhin Mutexes oder andere Synchronisationsmechanismen erforderlich. Übermäßiger oder falscher Einsatz atomarer Operationen kann zu schwer zu diagnostizierenden Fehlern oder Leistungseinbußen führen.