sync/atomic.Int64.CompareAndSwap în Go

Scrie valoarea nouă doar dacă valoarea curentă este cea așteptată; baza operațiilor atomice proprii, cu reîncercare.

Sintaxă
func (x *Int64) CompareAndSwap(old, new int64) (swapped bool)

Ce face sync/atomic.Int64.CompareAndSwap

n.CompareAndSwap(vechi, nou) face n = nou doar dacă n == vechi în acel moment și returnează true dacă a reușit. Modelul obișnuit este o buclă: citești cu Load, calculezi valoarea nouă, încerci CAS; dacă alt goroutine a schimbat valoarea între timp, CAS eșuează și reiei.

Așa poți scrie operații atomice care nu există gata făcute: scădere care nu trece sub zero, maxim, „pornește o singură dată”.

Parametri

Parametru Descriere
old Valoarea pe care te aștepți să o găsești.
new Valoarea de scris.

Ce returnează

true dacă valoarea a fost schimbată.

Exemple

Exemplul 1: Locuri care nu trec sub zero

30 de clienți încearcă să rezerve din 25 de locuri; exact 25 reușesc.

package main

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

func rezerva(locuri *atomic.Int64) bool {
	for {
		libere := locuri.Load()
		if libere == 0 {
			return false
		}
		if locuri.CompareAndSwap(libere, libere-1) {
			return true
		}
	}
}

func main() {
	var locuri, reusite atomic.Int64
	locuri.Store(25)
	var wg sync.WaitGroup
	for range 30 {
		wg.Go(func() {
			if rezerva(&locuri) {
				reusite.Add(1)
			}
		})
	}
	wg.Wait()
	fmt.Println("Rezervări reușite:", reusite.Load())
	fmt.Println("Locuri libere:", locuri.Load())
}

Rezultat

Rezervări reușite: 25
Locuri libere: 0

Exemplul 2: Rezultatul lui CompareAndSwap

package main

import (
	"fmt"
	"sync/atomic"
)

func main() {
	var versiune atomic.Int64
	versiune.Store(7)
	fmt.Println("7 -> 8:", versiune.CompareAndSwap(7, 8))
	fmt.Println("7 -> 9:", versiune.CompareAndSwap(7, 9))
	fmt.Println("Versiunea:", versiune.Load())
}

Rezultat

7 -> 8: true
7 -> 9: false
Versiunea: 8

Greșeli frecvente

  • Recalculezi valoarea nouă în afara buclei: dacă CAS eșuează, trebuie să citești din nou și să recalculezi, altfel bucla nu se termină niciodată.

Comenzi înrudite

  • sync/atomic.Int64: Un int64 cu operații atomice ca metode (Add, Load, Store, Swap); varianta recomandată pentru contoare între goroutine.
  • sync/atomic.CompareAndSwapInt64: CompareAndSwapInt64 scrie doar dacă valoarea e cea așteptată; SwapInt64 scrie și returnează valoarea veche.
  • sync/atomic.Bool: O valoare true/false citită și scrisă atomic; bună pentru steaguri ca „închis”, „pregătit” sau „rulează deja”.