sync/atomic.CompareAndSwapInt64 în Go

CompareAndSwapInt64 scrie doar dacă valoarea e cea așteptată; SwapInt64 scrie și returnează valoarea veche.

Sintaxă
func CompareAndSwapInt64(addr *int64, old, new int64) (swapped bool)
func SwapInt64(addr *int64, new int64) (old int64)

Ce face sync/atomic.CompareAndSwapInt64

atomic.CompareAndSwapInt64(&n, vechi, nou) (prescurtat CAS) verifică dacă n este încă vechi și, doar în acest caz, îl face nou; totul într-un singur pas atomic. Returnează true dacă a scris. Cu el construiești operații care nu există gata făcute, de exemplu „păstrează maximul”, într-o buclă care reîncearcă.

atomic.SwapInt64(&n, nou) pune valoarea nouă și returnează valoarea de dinainte. Este util pentru „citește și resetează”, de exemplu statistici trimise o dată pe minut.

Parametri

Parametru Descriere
addr Adresa variabilei.
old Valoarea așteptată (doar la CAS).
new Valoarea care se scrie.

Ce returnează

CompareAndSwapInt64: true dacă a scris. SwapInt64: valoarea veche.

Exemple

Exemplul 1: Maximul calculat din mai multe goroutine

Fiecare goroutine încearcă să pună valoarea lui dacă e mai mare. Dacă alt goroutine a schimbat maximul între citire și scriere, CAS eșuează și bucla reîncearcă.

package main

import (
	"fmt"
	"sync"
	"sync/atomic"
)

func actualizeazaMaxim(maxim *int64, v int64) {
	for {
		curent := atomic.LoadInt64(maxim)
		if v <= curent {
			return
		}
		if atomic.CompareAndSwapInt64(maxim, curent, v) {
			return
		}
	}
}

func main() {
	temperaturi := []int64{18, 23, 31, 27, 12, 29, 35, 22}
	var maxim int64
	var wg sync.WaitGroup
	for _, t := range temperaturi {
		wg.Go(func() { actualizeazaMaxim(&maxim, t) })
	}
	wg.Wait()
	fmt.Println("Temperatura maximă:", atomic.LoadInt64(&maxim), "°C")

	var stare int64 = 0
	fmt.Println("CAS 0->1:", atomic.CompareAndSwapInt64(&stare, 0, 1))
	fmt.Println("CAS 0->2:", atomic.CompareAndSwapInt64(&stare, 0, 2), "stare =", stare)
}

Rezultat

Temperatura maximă: 35 °C
CAS 0->1: true
CAS 0->2: false stare = 1

Exemplul 2: Citește și resetează cu SwapInt64

package main

import (
	"fmt"
	"sync/atomic"
)

func main() {
	var cereri int64
	for minut := 1; minut <= 3; minut++ {
		for range minut * 10 {
			atomic.AddInt64(&cereri, 1)
		}
		deTrimis := atomic.SwapInt64(&cereri, 0)
		fmt.Printf("Minutul %d: %d cereri (contor resetat la %d)\n", minut, deTrimis, atomic.LoadInt64(&cereri))
	}
}

Rezultat

Minutul 1: 10 cereri (contor resetat la 0)
Minutul 2: 20 cereri (contor resetat la 0)
Minutul 3: 30 cereri (contor resetat la 0)

Greșeli frecvente

  • Apelezi CAS o singură dată și ignori rezultatul false. Dacă alt goroutine a schimbat valoarea, modificarea ta nu s-a făcut; pune CAS într-o buclă.
  • Faci „citește și resetează” cu LoadInt64 urmat de StoreInt64(0). Între ele se pot pierde valori; folosește SwapInt64.

Comenzi înrudite

  • sync/atomic.AddInt64: Adună (AddInt64), citește (LoadInt64) și scrie (StoreInt64) un int64 atomic, sigur între goroutine, fără mutex.
  • sync/atomic.Int64.CompareAndSwap: Scrie valoarea nouă doar dacă valoarea curentă este cea așteptată; baza operațiilor atomice proprii, cu reîncercare.
  • sync.Map.CompareAndSwap: Schimbă (CompareAndSwap) sau șterge (CompareAndDelete) o cheie doar dacă valoarea ei este încă cea așteptată.