Sobes.tech
Middle

Πώς μπορεί να συγχρονιστεί η αλληλεπίδραση μεταξύ νημάτων;

sobes.tech AI

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

Υπάρχουν αρκετοί βασικοί μηχανισμοί συγχρονισμού και αλληλεπίδρασης μεταξύ νημάτων σε C/C++:

  • Μουτζούρες (Mutexes): Παρέχουν αποκλειστική πρόσβαση σε κοινόχρηστη πόρο. Ένα νήμα που κατέχει μια mutex μπλοκάρει άλλα νήματα που προσπαθούν να την αποκτήσουν μέχρι να την ελευθερώσει.

    #include <mutex>
    
    std::mutex my_mutex;
    
    void my_thread_function() {
        my_mutex.lock(); // Κατοχή mutex
        // Εργασία με τον κοινόχρηστο πόρο
        my_mutex.unlock(); // Απελευθέρωση mutex
    }
    

    Ή με χρήση RAII:

    #include <mutex>
    #include <lock_guard>
    
    std::mutex my_mutex;
    
    void my_thread_function() {
        std::lock_guard<std::mutex> lock(my_mutex); // Κατοχή mutex κατά τη δημιουργία του αντικειμένου
        // Εργασία με τον κοινόχρηστο πόρο
        // Το mutex απελευθερώνεται αυτόματα κατά την έξοδο από το scope
    }
    
  • Σεμάφορες (Semaphores): Γενίκευση των mutexes. Διατηρούν έναν μετρητή που επιτρέπει σε έναν ορισμένο αριθμό νημάτων να έχουν ταυτόχρονη πρόσβαση σε έναν πόρο.

    #include <semaphore.h> // Για POSIX σεμάφορες
    #include <windows.h> // Για Windows σεμάφορες
    
    sem_t my_semaphore; // POSIX σεμάφορ
    
    void my_thread_function() {
        sem_wait(&my_semaphore); // Μείωση μετρητή σεμάφορ
        // Εργασία με τον πόρο
        sem_post(&my_semaphore); // Αύξηση μετρητή σεμάφορ
    }
    

    (Σημείωση: Το πρότυπο C++ δεν περιλαμβάνει άμεσα σεμάφορες, αλλά διατίθενται μέσω βιβλιοθηκών συστήματος ή τρίτων.)

  • Μεταβλητές Συνθήκης (Condition Variables): Χρησιμοποιούνται ώστε ένα νήμα να μπορεί να περιμένει την εμφάνιση μιας συγκεκριμένης συνθήκης, απελευθερώνοντας ταυτόχρονα το mutex. Ένα άλλο νήμα μπορεί να ειδοποιήσει το αναμένον νήμα ότι η συνθήκη εκπληρώθηκε.

    #include <mutex>
    #include <condition_variable>
    
    std::mutex my_mutex;
    std::condition_variable my_cv;
    bool condition_met = false;
    
    void waiting_thread() {
        std::unique_lock<std::mutex> lock(my_mutex);
        my_cv.wait(lock, []{ return condition_met; }); // Αναμονή για την εκπλήρωση της συνθήκης
        // Η συνθήκη εκπληρώθηκε, εργασία με τον πόρο
    }
    
    void notifying_thread() {
        std::unique_lock<std::mutex> lock(my_mutex);
        condition_met = true;
        lock.unlock(); // Απελευθέρωση του mutex πριν την ειδοποίηση (προαιρετικό, αλλά συνιστάται)
        my_cv.notify_one(); // Ειδοποίηση ενός νήματος που περιμένει
        // ή
        // my_cv.notify_all(); // Ειδοποίηση όλων των νήματων που περιμένουν
    }
    
  • Ατομικές λειτουργίες (Atomic Operations): Επιτρέπουν την εκτέλεση απλών λειτουργιών σε μεταβλητές (π.χ., αύξηση, μείωση) ως μια ενιαία, μη διαιρετή ενέργεια, χωρίς να απαιτείται ρητή κλειδώματος.

    #include <atomic>
    
    std::atomic<int> counter(0);
    
    void my_thread_function() {
        counter++; // Ατομική αύξηση
        // Ή:
        // counter.fetch_add(1);
    }
    
  • Κλειδώματα ανάγνωσης-εγγραφής (Shared Mutexes): Επιτρέπουν σε πολλά νήματα να διαβάζουν δεδομένα ταυτόχρονα, αλλά μόνο σε ένα να γράφει.

    #include <shared_mutex> // C++17 και άνω
    
    std::shared_mutex my_shared_mutex;
    
    void read_thread() {
        my_shared_mutex.lock_shared(); // Κατοχή για ανάγνωση
        // Ανάγνωση δεδομένων
        my_shared_mutex.unlock_shared(); // Απελευθέρωση για ανάγνωση
    }
    
    void write_thread() {
        my_shared_mutex.lock(); // Κατοχή για εγγραφή (αποκλειστική)
        // Εγγραφή δεδομένων
        my_shared_mutex.unlock(); // Απελευθέρωση για εγγραφή
    }
    
  • Φράγματα (Barriers): Επιτρέπουν σε μια ομάδα νημάτων να περιμένουν μέχρι όλα να φτάσουν σε ένα συγκεκριμένο σημείο στην εκτέλεσή τους, πριν συνεχίσουν.

    #include <barrier> // C++20
    
    std::barrier sync_point(4); // Φράγμα για 4 νήματα
    
    void my_thread_function() {
        // Εργασία πριν από το φράγμα
        sync_point.arrive_and_wait(); // Αναμονή μέχρι όλα τα νήματα να φτάσουν στο φράγμα
        // Εργασία μετά το φράγμα
    }
    

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