Sobes.tech
Middle+

Τι είναι οι ατομικές λειτουργίες, ποιοι τύποι υπάρχουν και πότε και πώς είναι καλύτερο να τις χρησιμοποιείτε;

sobes.tech AI

Απάντηση από AI

Ατομικές λειτουργίες είναι λειτουργίες στη μνήμη, η εκτέλεση των οποίων ολοκληρώνεται εγγυημένα είτε πλήρως είτε καθόλου, και δεν μπορούν να διακοπούν από άλλες ανταγωνιστικές λειτουργίες. Είναι αδιαίρετες και ασφαλείς σε επίπεδο υλικού.

Στο Go, το πακέτο sync/atomic παρέχει μια σειρά από συναρτήσεις για την εκτέλεση ατομικών λειτουργιών:

Συνάρτηση Περιγραφή
AddInt32 Προσθέτει μια τιμή ατομικά στο int32.
AddInt64 Προσθέτει μια τιμή ατομικά στο int64.
AddUint32 Προσθέτει μια τιμή ατομικά στο uint32.
AddUint64 Προσθέτει μια τιμή ατομικά στο uint64.
AddUintptr Προσθέτει μια τιμή ατομικά στο uintptr.
CompareAndSwapInt32 Συγκρίνει και ανταλλάσσει ατομικά (CAS) για το int32.
CompareAndSwapInt64 Συγκρίνει και ανταλλάσσει ατομικά (CAS) για το int64.
CompareAndSwapUint32 Συγκρίνει και ανταλλάσσει ατομικά (CAS) για το uint32.
CompareAndSwapUint64 Συγκρίνει και ανταλλάσσει ατομικά (CAS) για το uint64.
CompareAndSwapUintptr Συγκρίνει και ανταλλάσσει ατομικά (CAS) για το uintptr.
CompareAndSwapPointer Συγκρίνει και ανταλλάσσει ατομικά (CAS) για το unsafe.Pointer.
LoadInt32 Φορτώνει ατομικά την τιμή int32.
LoadInt64 Φορτώνει ατομικά την τιμή int64.
LoadUint32 Φορτώνει ατομικά την τιμή uint32.
LoadUint64 Φορτώνει ατομικά την τιμή uint64.
LoadUintptr Φορτώνει ατομικά την τιμή uintptr.
LoadPointer Φορτώνει ατομικά την τιμή unsafe.Pointer.
StoreInt32 Αποθηκεύει ατομικά την τιμή int32.
StoreInt64 Αποθηκεύει ατομικά την τιμή int64.
StoreUint32 Αποθηκεύει ατομικά την τιμή uint32.
StoreUint64 Αποθηκεύει ατομικά την τιμή uint64.
StoreUintptr Αποθηκεύει ατομικά την τιμή uintptr.
StorePointer Αποθηκεύει ατομικά την τιμή unsafe.Pointer.
SwapInt32 Ατομικά ανταλλάσσει την τιμή int32.
SwapInt64 Ατομικά ανταλλάσσει την τιμή int64.
SwapUint32 Ατομικά ανταλλάσσει την τιμή uint32.
SwapUint64 Ατομικά ανταλλάσσει την τιμή uint64.
SwapUintptr Ατομικά ανταλλάσσει την τιμή uintptr.
SwapPointer Ατομικά ανταλλάσσει την τιμή unsafe.Pointer.

Η χρήση ατομικών λειτουργιών είναι η καλύτερη επιλογή σε περιπτώσεις όπου απαιτείται απλός, χαμηλού επιπέδου συγχρονισμός πρόσβασης σε κοινές μεταβλητές, συχνά μετρητές ή σημαίες, χωρίς τη χρήση mutex. Παρέχουν ελάχιστο overhead και καλύτερη απόδοση σε τέτοια σενάρια.

Παράδειγμα χρήσης ατομικού μετρητή:

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() {
			// Ατομικά αυξάνουμε τον μετρητή
			atomic.AddInt64(&counter, 1)
			wg.Done()
		}()
	}

	wg.Wait()
	// Ατομικά φορτώνουμε την τιμή του μετρητή
	fmt.Println("Final Counter:", atomic.LoadInt64(&counter))
}

Παράδειγμα χρήσης CompareAndSwap:

package main

import (
	"fmt"
	"sync/atomic"
)

func main() {
	var value int32 = 10

	// Προσπάθεια ανταλλαγής αν η τρέχουσα τιμή είναι 10, σε 20
	swapped := atomic.CompareAndSwapInt32(&value, 10, 20)
	fmt.Println("Swapped:", swapped) // Swapped: true
	fmt.Println("Value:", value)   // Value: 20

	// Προσπάθεια ανταλλαγής αν η τρέχουσα τιμή είναι 10, σε 30
	swapped = atomic.CompareAndSwapInt32(&value, 10, 30)
	fmt.Println("Swapped:", swapped) // Swapped: false
	fmt.Println("Value:", value)   // Value: 20
}

Σημαντικό: οι ατομικές λειτουργίες λειτουργούν μόνο με primitive τύπους δεδομένων (ακέραιοι, δείκτες). Για συγχρονισμό πρόσβασης σε πιο πολύπλοκες δομές δεδομένων ή εκτέλεση μπλοκαριστικών λειτουργιών, απαιτούνται mutex ή άλλα μηχανισμοί συγχρονισμού. Η υπερβολική ή λανθασμένη χρήση ατομικών λειτουργιών μπορεί να οδηγήσει σε δυσδιάκριτα σφάλματα ή μείωση της απόδοσης.