sync/atomic.Bool în Go

O valoare true/false citită și scrisă atomic; bună pentru steaguri ca „închis”, „pregătit” sau „rulează deja”.

Sintaxă
var b atomic.Bool
func (x *Bool) Load() bool
func (x *Bool) Store(val bool)
func (x *Bool) Swap(new bool) (old bool)
func (x *Bool) CompareAndSwap(old, new bool) (swapped bool)

Ce face sync/atomic.Bool

atomic.Bool ține un bool care poate fi citit și scris din mai multe goroutine fără cursă de date. Valoarea zero este false. Load citește, Store scrie, Swap scrie și returnează valoarea veche, iar CompareAndSwap(false, true) schimbă valoarea doar dacă era false.

CompareAndSwap(false, true) este un mod simplu de a face „doar primul trece”: exact un goroutine primește true.

Ce returnează

Load: valoarea. Swap: valoarea veche. CompareAndSwap: dacă a schimbat. Store: nimic.

Exemple

Exemplul 1: Serviciu închis o singură dată

package main

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

type Serviciu struct {
	inchis atomic.Bool
}

func (s *Serviciu) Inchide() bool {
	return s.inchis.CompareAndSwap(false, true)
}

func main() {
	s := &Serviciu{}
	var inchideriReale atomic.Int32
	var wg sync.WaitGroup
	for range 10 {
		wg.Go(func() {
			if s.Inchide() {
				inchideriReale.Add(1)
			}
		})
	}
	wg.Wait()
	fmt.Println("Închis:", s.inchis.Load())
	fmt.Println("Închideri efectuate:", inchideriReale.Load())
}

Rezultat

Închis: true
Închideri efectuate: 1

Exemplul 2: Store, Load și Swap

package main

import (
	"fmt"
	"sync/atomic"
)

func main() {
	var mentenanta atomic.Bool
	fmt.Println("Valoare inițială:", mentenanta.Load())
	mentenanta.Store(true)
	fmt.Println("După Store(true):", mentenanta.Load())
	vechi := mentenanta.Swap(false)
	fmt.Println("Swap(false) a returnat:", vechi, "- acum:", mentenanta.Load())
}

Rezultat

Valoare inițială: false
După Store(true): true
Swap(false) a returnat: true - acum: false

Greșeli frecvente

  • Folosești un bool obișnuit ca semnal de oprire între goroutine (for !stop {}). Fără sincronizare, compilatorul poate presupune că valoarea nu se schimbă și bucla nu se mai oprește; folosește atomic.Bool, un canal sau context.

Comenzi înrudite

  • sync/atomic.Int32: Un int32 cu operații atomice (Add, Load, Store, Swap, CompareAndSwap); la fel ca atomic.Int64, dar pe 32 de biți.
  • sync.Once: Garantează că o funcție rulează o singură dată, chiar dacă Do este apelat de multe goroutine (inițializare leneșă).
  • context.WithCancel: Creează un context copil și o funcție cancel; apelul cancel() anunță toate goroutine-urile care îl folosesc să se oprească.