Sobes.tech
Middle+

Kas yra atomarinės operacijos, kokie jų tipai ir kada ir kaip geriausia jas naudoti?

sobes.tech AI

Atsakymas iš AI

Atomarinės operacijos yra operacijos su atmintimi, kurių vykdymas garantuojamai užbaigiamas arba visiškai, arba visai nepradedamas, ir jos negali būti nutrauktos kitų konkuruojančių operacijų. Jos yra nepadalomos ir saugios keliuose srautuose aparatinės įrangos lygiu.

Go paketas sync/atomic teikia funkcijų rinkinį atomarinių operacijų vykdymui:

Funkcija Aprašymas
AddInt32 Atomariškai prideda reikšmę prie int32.
AddInt64 Atomariškai prideda reikšmę prie int64.
AddUint32 Atomariškai prideda reikšmę prie uint32.
AddUint64 Atomariškai prideda reikšmę prie uint64.
AddUintptr Atomariškai prideda reikšmę prie uintptr.
CompareAndSwapInt32 Atomariškai lygina ir keičia (CAS) int32.
CompareAndSwapInt64 Atomariškai lygina ir keičia (CAS) int64.
CompareAndSwapUint32 Atomariškai lygina ir keičia (CAS) uint32.
CompareAndSwapUint64 Atomariškai lygina ir keičia (CAS) uint64.
CompareAndSwapUintptr Atomariškai lygina ir keičia (CAS) uintptr.
CompareAndSwapPointer Atomariškai lygina ir keičia (CAS) unsafe.Pointer.
LoadInt32 Atomariškai įkelia reikšmę int32.
LoadInt64 Atomariškai įkelia reikšmę int64.
LoadUint32 Atomariškai įkelia reikšmę uint32.
LoadUint64 Atomariškai įkelia reikšmę uint64.
LoadUintptr Atomariškai įkelia reikšmę uintptr.
LoadPointer Atomariškai įkelia reikšmę unsafe.Pointer.
StoreInt32 Atomariškai išsaugo reikšmę int32.
StoreInt64 Atomariškai išsaugo reikšmę int64.
StoreUint32 Atomariškai išsaugo reikšmę uint32.
StoreUint64 Atomariškai išsaugo reikšmę uint64.
StoreUintptr Atomariškai išsaugo reikšmę uintptr.
StorePointer Atomariškai išsaugo reikšmę unsafe.Pointer.
SwapInt32 Atomariškai keičia reikšmes int32.
SwapInt64 Atomariškai keičia reikšmes int64.
SwapUint32 Atomariškai keičia reikšmes uint32.
SwapUint64 Atomariškai keičia reikšmes uint64.
SwapUintptr Atomariškai keičia reikšmes uintptr.
SwapPointer Atomariškai keičia reikšmes unsafe.Pointer.

Naudoti atomarines operacijas yra geriausia tada, kai reikia paprastos, žemo lygio prieigos prie bendrų kintamųjų sinchronizacijos, dažniausiai skaitiklių ar žymeklių, be naudotojų užrakto. Jos užtikrina minimalias išlaidas ir geresnį našumą nei užraktai tokiose scenarijuose.

Pavyzdys naudojant atomarinį skaitiklį:

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() {
			// Atomariškai padidiname skaitiklį
			atomic.AddInt64(&counter, 1)
			wg.Done()
		}()
	}

	wg.Wait()
	// Atomariškai įkelia reikšmę skaitiklio
	fmt.Println("Galutinis skaitiklis:", atomic.LoadInt64(&counter))
}

Pavyzdys naudojant CompareAndSwap:

package main

import (
	"fmt"
	"sync/atomic"
)

func main() {
	var value int32 = 10

	// Bandymas pakeisti reikšmę, jei dabartinė yra 10, į 20
	swapped := atomic.CompareAndSwapInt32(&value, 10, 20)
	fmt.Println("Pakeista:", swapped) // Pakeista: true
	fmt.Println("Reikšmė:", value)   // Reikšmė: 20

	// Bandymas pakeisti reikšmę, jei dabartinė yra 10, į 30
	swapped = atomic.CompareAndSwapInt32(&value, 10, 30)
	fmt.Println("Pakeista:", swapped) // Pakeista: false
	fmt.Println("Reikšmė:", value)   // Reikšmė: 20
}

Svarbu prisiminti, kad atomarinės operacijos veikia su primityviais duomenų tipais (visas skaičius, nuorodos). Daugiau sudėtingų duomenų struktūrų ar blokuojančių operacijų sinchronizacijai vis dar reikalingi užraktai ar kiti sinchronizacijos mechanizmai. Pernelyg didelis ar netinkamas atomarinių operacijų naudojimas gali sukelti sunkiai aptinkamas klaidas arba sumažinti našumą.