How to synchronize data between threads and ensure context consistency?
sobes.tech AI
Answer from AI
The following approaches are used for data synchronization and ensuring context consistency in a multithreaded iOS application:
-
NSLock / Lock: Simple mutexes for locking critical sections of code.
import Foundation class ThreadSafeCounter { private var count: Int = 0 private let lock = NSLock() func increment() { lock.lock() defer { lock.unlock() } // Guaranteed lock release count += 1 } func getCount() -> Int { lock.lock() defer { lock.unlock() } return count } } -
NSRecursiveLock: Allows re-locking from the same thread.
import Foundation class RecursiveLocker { private let lock = NSRecursiveLock() func firstMethod() { lock.lock() defer { lock.unlock() } print("First method") secondMethod() // Re-locking is possible } func secondMethod() { lock.lock() defer { lock.unlock() } print("Second method") } } -
NSCondition / NSConditionLock: Allows threads to wait for a specific condition or value before continuing execution. Used for implementing producer-consumer pattern.
import Foundation class Buffer { private var items: [Int] = [] private let condition = NSCondition() private let capacity = 5 func addItem(_ item: Int) { condition.lock() defer { condition.unlock() } while items.count == capacity { condition.wait() // Wait until there is free space } items.append(item) print("Added: \(item), Current Buffer: \(items)") condition.signal() // Signal waiting threads } func removeItem() -> Int { condition.lock() defer { condition.unlock() } while items.isEmpty { condition.wait() // Wait until an element appears } let item = items.removeFirst() print("Removed: \(item), Current Buffer: \(items)") condition.signal() // Signal waiting threads return item } } -
Grand Central Dispatch (GCD):
-
Serial Queues: Execute tasks sequentially, one at a time on the given thread, ensuring atomic access to resources.
-
Concurrent Queues with Barrier Flags: Allow concurrent reads, but write operations are executed exclusively. Implements a read-write lock model.
import Foundation class ThreadSafeArray<Element> { private var array: [Element] = [] private let queue = DispatchQueue(label: "com.mycompany.threadsafearray", attributes: .concurrent) // Concurrent queue func append(_ newElement: Element) { queue.async(flags: .barrier) { // Barrier for writing self.array.append(newElement) } } func get(at index: Int) -> Element? { var result: Element? queue.sync { // Sync for reading (or async if read order doesn't matter) if index < self.array.count { result = self.array[index] } } return result } }
-
-
Core Data Context Operations: Each
NSManagedObjectContexthas its own private serial queue. Access to objects and the context should be performed only from this thread.import CoreData // Example of saving in a Core Data context let context = // Obtained context context.perform { // Execute on the context's private thread // Work with Core Data objects here do { try context.save() } catch { // Error handling } }
The choice of method depends on the specific task and performance requirements. GCD with serial queues or concurrent queues with barriers is often the most preferred approach due to its efficiency and ease of use. For Core Data, always use the perform or performAndWait methods.