Sobes.tech
Middle

Τι είναι η κατάσταση αγώνα (race condition);

sobes.tech AI

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

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

Παράδειγμα σε iOS: Ας υποθέσουμε ότι έχουμε έναν μετρητή count, στον οποίο προσπελαύνουν ταυτόχρονα δύο νήματα για να τον αυξήσουν.

var count = 0

func incrementCount() {
    // Το νήμα 1 διαβάζει count = 5
    // Το νήμα 2 διαβάζει count = 5
    let currentCount = count
    // Το νήμα 1 υπολογίζει newCount = 6
    // Το νήμα 2 υπολογίζει newCount = 6
    let newCount = currentCount + 1
    // Το νήμα 1 γράφει count = 6
    // Το νήμα 2 γράφει count = 6 (αντικαθιστώντας το αποτέλεσμα του νήματος 1)
    count = newCount
}

// Αν δύο νήματα καλέσουν το incrementCount() ταυτόχρονα,
// το αναμενόμενο αποτέλεσμα θα ήταν count = 2,
// αλλά λόγω της κατάστασης αγώνα, το count μπορεί να γίνει 1.

Για την αποτροπή καταστάσεων αγώνα, χρησιμοποιούνται μηχανισμοί συγχρονισμού:

  • Lock/Mu: Μπλοκάρει την πρόσβαση στον πόρο όσο το νήμα τον χρησιμοποιεί.
    import Foundation
    
    let lock = NSLock()
    var count = 0
    
    func incrementCount() {
        lock.lock() // Απόκτηση κλειδώματος
        let currentCount = count
        let newCount = currentCount + 1
        count = newCount
        lock.unlock() // Απελευθέρωση κλειδώματος
    }
    
  • Ουρές (Queues): Χρήση σειριακών ουρών GCD.
    import Foundation
    
    let serialQueue = DispatchQueue(label: "com.myapp.counterQueue")
    var count = 0
    
    func incrementCount() {
        serialQueue.async {
            let currentCount = count
            let newCount = currentCount + 1
            count = newCount
        }
    }
    
  • Ατομικές λειτουργίες: Μη διαχωρίσιμες λειτουργίες σε επίπεδο εντολών επεξεργαστή. Στη Swift, μπορείτε να χρησιμοποιήσετε OSAtomic... (παλιό) ή Atomic από @_implementationOnly import Atomics (πειραματικό), ή προσεγγίσεις βασισμένες σε κλειδώματα/ουρές.

Η επιλογή της μεθόδου συγχρονισμού εξαρτάται από το συγκεκριμένο σενάριο και τις απαιτήσεις απόδοσης. Είναι σημαντικό να κατανοήσετε ότι ο συγχρονισμός μπορεί να προσθέσει επιπλέον φόρτο και να οδηγήσει σε αμοιβαίες αποκλεισμούς (deadlocks) αν υλοποιηθεί λανθασμένα.