Middle+
Πώς λύνεται το πρόβλημα του ανταγωνισμού (race condition);
sobes.tech AI
Απάντηση από AI
Χρήση συγχρονισμού ροών πρόσβασης σε κοινόχρηστους πόρους.
Βασικές προσεγγίσεις:
- Mutexes (Μαζί): Κλειδώνουν την πρόσβαση σε πόρο για άλλες ροές, ενώ μια ροή τον χρησιμοποιεί.
NSLockos_unfair_lockpthread_mutex_t
import Foundation
let lock = NSLock()
var sharedResource = 0
func modifyResource() {
lock.lock() // Αποκτάμε το mutex
// Κρίσιμη ενότητα - ασφαλής πρόσβαση στο sharedResource
sharedResource += 1
print("Τιμή πόρου: \(sharedResource)")
lock.unlock() // Απελευθερώνουμε το mutex
}
// Παράδειγμα κλήσης από διαφορετικές ροές
// DispatchQueue.global().async { modifyResource() }
// DispatchQueue.global().async { modifyResource() }
- Ακολουθίες (Serial Queues): Εκτελούν εργασίες μία-μία, διασφαλίζοντας ότι μόνο ένα μπλοκ κώδικα έχει πρόσβαση σε πόρο ταυτόχρονα.
DispatchQueue.mainDispatchQueue.global()με χαρακτηριστικό.serial
import Foundation
let serialQueue = DispatchQueue(label: "com.example.serialQueue")
var sharedResource = 0
func modifyResourceAsync() {
serialQueue.async {
// Ο κώδικας εκτελείται διαδοχικά
sharedResource += 1
print("Τιμή πόρου: \(sharedResource)")
}
}
// Παράδειγμα κλήσης από διαφορετικές ροές/ουρές
// modifyResourceAsync()
// modifyResourceAsync()
- Αναγνώστης-Γράφτης Κλειδώματα (Reader-Writer Locks): Επιτρέπουν σε πολλές ροές να διαβάζουν ταυτόχρονα, αλλά μόνο μία να γράφει.
DispatchQueueμε barriers (.barrier) για εγγραφή και συγχρονισμένη/ασύγχρονη πρόσβαση για ανάγνωση.
import Foundation
let concurrentQueue = DispatchQueue(label: "com.example.concurrentQueue", attributes: .concurrent)
var sharedResource = 0
func readResource() {
concurrentQueue.async { // Ανάγνωση - πολλοί αναγνώστες ταυτόχρονα
print("Ανάγνωση πόρου: \(sharedResource)")
}
}
func writeResource(newValue: Int) {
concurrentQueue.sync(flags: .barrier) { // Εγγραφή - μόνο ένας συγγραφέας και κανείς άλλος δεν διαβάζει/γράφει
sharedResource = newValue
print("Γράφοντας πόρο: \(sharedResource)")
}
}
// Παράδειγμα κλήσεων
// readResource()
// readResource()
// writeResource(newValue: 10)
// readResource()
- Ατομικές Ενέργειες (Atomic Operations): Ενέργειες που εκτελούνται πλήρως και χωρίς διακοπή από άλλη ροή. Χρησιμοποιούνται για απλούς τύπους δεδομένων (μετρητές, σημαίες).
OSAtomicIncrement,OSAtomicDecrement(Παλαιότερα, αλλά η ιδέα παραμένει актуальна)- C++11
<atomic>
#include <atomic>
#include <iostream>
std::atomic<int> atomicCounter(0);
void incrementCounter() {
atomicCounter++; // Ατομική αύξηση
std::cout << "Μετρητής: " << atomicCounter << std::endl;
}
// Παράδειγμα C++ στο πλαίσιο του iOS (χρήση με Objective-C++)
// Κάλεσε από διαφορετικές ροές
- Τύποι Αξιών (Value Types): Η μεταφορά αντιγράφων δομών/περιγραφών αντί για αναφορές σε κλάσεις μπορεί να βοηθήσει στην αποφυγή προβλημάτων με τον ανταγωνισμό δεδομένων, αν τα δεδομένα δεν μοιράζονται άμεσα μεταξύ των ροών σε μεταβλητή μορφή.
Η επιλογή της μεθόδου εξαρτάται από τη φύση της εργασίας και το απαιτούμενο επίπεδο παράλληλης εκτέλεσης. Για τις περισσότερες εργασίες στο iOS, προτιμώνται DispatchQueue (Serial και Concurrent με barriers) λόγω ευκολίας χρήσης και ενσωμάτωσης με το Grand Central Dispatch (GCD).