Sobes.tech
Middle+

Cosa sono le operazioni atomiche, quali sono i loro tipi e quando e come è meglio usarle?

sobes.tech AI

Risposta dell'AI

Le operazioni atomiche sono operazioni sulla memoria che garantiscono che vengano completate completamente o non vengano affatto avviate, e non possono essere interrotte da altre operazioni concorrenti. Sono indivisibili e sicure per i thread a livello hardware.

In Go, il pacchetto sync/atomic fornisce un insieme di funzioni per eseguire operazioni atomiche:

Funzione Descrizione
AddInt32 Aggiunge in modo atomico un valore a int32.
AddInt64 Aggiunge in modo atomico un valore a int64.
AddUint32 Aggiunge in modo atomico un valore a uint32.
AddUint64 Aggiunge in modo atomico un valore a uint64.
AddUintptr Aggiunge in modo atomico un valore a uintptr.
CompareAndSwapInt32 Confronta e scambia (CAS) int32 in modo atomico.
CompareAndSwapInt64 Confronta e scambia (CAS) int64 in modo atomico.
CompareAndSwapUint32 Confronta e scambia (CAS) uint32.
CompareAndSwapUint64 Confronta e scambia (CAS) uint64.
CompareAndSwapUintptr Confronta e scambia (CAS) uintptr.
CompareAndSwapPointer Confronta e scambia (CAS) unsafe.Pointer.
LoadInt32 Carica in modo atomico il valore di int32.
LoadInt64 Carica in modo atomico il valore di int64.
LoadUint32 Carica in modo atomico il valore di uint32.
LoadUint64 Carica in modo atomico il valore di uint64.
LoadUintptr Carica in modo atomico il valore di uintptr.
LoadPointer Carica in modo atomico il valore di unsafe.Pointer.
StoreInt32 Memorizza in modo atomico il valore di int32.
StoreInt64 Memorizza in modo atomico il valore di int64.
StoreUint32 Memorizza in modo atomico il valore di uint32.
StoreUint64 Memorizza in modo atomico il valore di uint64.
StoreUintptr Memorizza in modo atomico il valore di uintptr.
StorePointer Memorizza in modo atomico il valore di unsafe.Pointer.
SwapInt32 Scambia in modo atomico i valori di int32.
SwapInt64 Scambia in modo atomico i valori di int64.
SwapUint32 Scambia in modo atomico i valori di uint32.
SwapUint64 Scambia in modo atomico i valori di uint64.
SwapUintptr Scambia in modo atomico i valori di uintptr.
SwapPointer Scambia in modo atomico i valori di unsafe.Pointer.

È consigliabile usare operazioni atomiche nei casi in cui è richiesta una semplice sincronizzazione a basso livello per l'accesso a variabili condivise, come contatori o flag, senza usare mutex. Offrono costi minimi e migliori prestazioni in tali scenari.

Esempio di utilizzo di un contatore atomico:

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() {
			// Incrementa il contatore in modo atomico
			atomic.AddInt64(&counter, 1)
			wg.Done()
		}()
	}

	wg.Wait()
	// Carica in modo atomico il valore del contatore
	fmt.Println("Contatore finale:", atomic.LoadInt64(&counter))
}

Esempio di utilizzo di CompareAndSwap:

package main

import (
	"fmt"
	"sync/atomic"
)

func main() {
	var value int32 = 10

	// Tentativo di scambiare il valore se attualmente è 10, in 20
	swapped := atomic.CompareAndSwapInt32(&value, 10, 20)
	fmt.Println("Scambiato:", swapped) // true
	fmt.Println("Valore:", value)   // 20

	// Tentativo di scambiare il valore se attualmente è 10, in 30
	swapped = atomic.CompareAndSwapInt32(&value, 10, 30)
	fmt.Println("Scambiato:", swapped) // false
	fmt.Println("Valore:", value)   // 20
}

Ricorda che le operazioni atomiche funzionano con tipi di dati primitivi (numeri interi, puntatori). Per la sincronizzazione di strutture dati più complesse o operazioni bloccanti, sono ancora necessari mutex o altri meccanismi di sincronizzazione. Un uso eccessivo o scorretto di operazioni atomiche può portare a errori difficili da rilevare o a riduzioni delle prestazioni.