Как да синхронизираме данните между нишки и да осигурим съгласуваност на контекста?
sobes.tech AI
Отговор от AI
За синхронизиране на данните и осигуряване на съгласуваност на контекста в многонишково приложение на iOS се използват следните подходи:
-
NSLock / Lock: Простички мьютекси за блокиране на критични секции от код.
import Foundation class ThreadSafeCounter { private var count: Int = 0 private let lock = NSLock() func increment() { lock.lock() defer { lock.unlock() } // Гарантирано освобождаване на блокировката count += 1 } func getCount() -> Int { lock.lock() defer { lock.unlock() } return count } } -
NSRecursiveLock: Позволява повторно захващане на блокировката от същия поток.
import Foundation class RecursiveLocker { private let lock = NSRecursiveLock() func firstMethod() { lock.lock() defer { lock.unlock() } print("Първи метод") secondMethod() // Повторно захващане е възможно } func secondMethod() { lock.lock() defer { lock.unlock() } print("Втори метод") } } -
NSCondition / NSConditionLock: Позволява на нишките да чакат определено условие (Condition) или стойност (ConditionLock) преди да продължат изпълнението. Използва се за реализиране на producer-consumer модел.
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() // Чака, докато се освободи място } items.append(item) print("Добавено: \(item), Текущ буфер: \(items)") condition.signal() // Сигнализира чакащите нишки } func removeItem() -> Int { condition.lock() defer { condition.unlock() } while items.isEmpty { condition.wait() // Чака, докато се появи елемент } let item = items.removeFirst() print("Премахнато: \(item), Текущ буфер: \(items)") condition.signal() // Сигнализира чакащите нишки return item } } -
Grand Central Dispatch (GCD):
-
Serial Queues: Изпълняват задачите последователно, една по една на даден поток, което осигурява атомарен достъп до ресурсите.
-
Concurrent Queues с Barrier Flags: Позволяват изпълнение на четене паралелно, но операциите по писане (write) се изпълняват изключително. Това реализира модел read-write lock.
import Foundation class ThreadSafeArray<Element> { private var array: [Element] = [] private let queue = DispatchQueue(label: "com.mycompany.threadsafearray", attributes: .concurrent) // Конкурентна опашка func append(_ newElement: Element) { queue.async(flags: .barrier) { // Бариера за писане self.array.append(newElement) } } func get(at index: Int) -> Element? { var result: Element? queue.sync { // Синхронизиране за четене (или async, ако редът на четене не е важен) if index < self.array.count { result = self.array[index] } } return result } }
-
-
Core Data Context операции: Всеки
NSManagedObjectContextима свой собствен частен сериален поток. Достъпът до обектите и контекста трябва да се извършва само от този поток.import CoreData // Пример за запазване в контекста на Core Data let context = // Получен контекст context.perform { // Изпълнение на приватен поток на контекста // Работа с обекти на Core Data тук do { try context.save() } catch { // Обработка на грешка } }
Изборът на метод зависи от конкретната задача и изискванията за производителност. GCD с серийни опашки или паралелни опашки с бариери често е най-препоръчителният подход поради ефективността и удобството при използване. За Core Data винаги използвайте методите perform или performAndWait.