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.