Sobes.tech
Middle

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

sobes.tech AI

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

Ο αμοιβαίος αποκλεισμός (από τα αγγλικά MUTual EXclusion) είναι μια primitive συγχρονισμού που χρησιμοποιείται για την εξασφάλιση αποκλειστικής πρόσβασης σε κοινόχρηστο πόρο μεταξύ ανταγωνιστικών goroutines. Διασφαλίζει ότι σε κάθε χρονική στιγμή μόνο μια goroutine μπορεί να κατέχει τον αμοιβαίο αποκλεισμό και να έχει πρόσβαση στα προστατευόμενα δεδομένα.

Στο Go, το πακέτο sync παρέχει δύο βασικούς τύπους mutex:

  • sync.Mutex: Τυπικός mutex. Παρέχει τις μεθόδους Lock() και Unlock(). Αν ο mutex ήδη είναι κλειδωμένος, η κλήση Lock() μπλοκάρει την τρέχουσα goroutine μέχρι να απελευθερωθεί. Το Unlock() ξεκλειδώνει τον mutex.

    var mu sync.Mutex
    var counter int
    
    func increment() {
        mu.Lock() // Κλειδώνει την πρόσβαση στο counter
        counter++
        mu.Unlock() // Ξεκλειδώνει την πρόσβαση
    }
    
  • sync.RWMutex: Mutex για ανάγνωση/εγγραφή. Επιτρέπει σε πολλαπλές goroutines να διαβάζουν ταυτόχρονα τον προστατευόμενο πόρο, αλλά μόνο μία να γράφει. Παρέχει τις μεθόδους Lock(), Unlock() (για εγγραφή), RLock() και RUnlock() (για ανάγνωση).

    var rwMu sync.RWMutex
    var data []int
    
    func readData() {
        rwMu.RLock() // Κλειδώνει για ανάγνωση
        // Ανάγνωση data
        fmt.Println(data)
        rwMu.RUnlock() // Ξεκλειδώνει την ανάγνωση
    }
    
    func writeData(val int) {
        rwMu.Lock() // Κλειδώνει για εγγραφή
        // Εγγραφή σε data
        data = append(data, val)
        rwMu.Unlock() // Ξεκλειδώνει την εγγραφή
    }
    

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

Κανόνες χρήσης:

  1. Καλείτε Lock() πριν την πρόσβαση στον προστατευόμενο πόρο.
  2. Καλείτε Unlock() μετά το τέλος της πρόσβασης. Συνιστάται η χρήση defer mu.Unlock() για την εγγύηση της απελευθέρωσης ακόμα και σε περίπτωση πανικού.
  3. Μην προσπαθείτε να κλειδώσετε ήδη κλειδωμένο mutex από την ίδια goroutine (μπορεί να οδηγήσει σε deadlock).
  4. Μην προσπαθείτε να ξεκλειδώσετε ένα μη κλειδωμένο mutex.
  5. Κατά τη χρήση RWMutex, χρησιμοποιείτε RLock/RUnlock για αναγνώσεις και Lock/Unlock για εγγραφές.
  6. Τα mutex δεν πρέπει να αντιγράφονται μετά την πρώτη χρήση. Τα περνάτε μέσω αναφοράς ή δείκτη.

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

func safeIncrement() {
    mu.Lock()
    defer mu.Unlock() // Εγγυάται την απελευθέρωση
    counter++
}