Como sincronizar dados entre threads e garantir a consistência do contexto?
sobes.tech IA
Resposta da IA
Para sincronizar dados e garantir a coerência do contexto numa aplicação multithread no iOS, utilizam-se as seguintes abordagens:
-
NSLock / Lock: Mutex simples para bloquear secções críticas do código.
import Foundation class ThreadSafeCounter { private var count: Int = 0 private let lock = NSLock() func increment() { lock.lock() defer { lock.unlock() } // Liberação garantida do bloqueio count += 1 } func getCount() -> Int { lock.lock() defer { lock.unlock() } return count } } -
NSRecursiveLock: Permite readquirir o bloqueio a partir do mesmo thread.
import Foundation class RecursiveLocker { private let lock = NSRecursiveLock() func firstMethod() { lock.lock() defer { lock.unlock() } print("Primeiro método") secondMethod() // A reaquisição é possível } func secondMethod() { lock.lock() defer { lock.unlock() } print("Segundo método") } } -
NSCondition / NSConditionLock: Permitem que threads aguardem uma condição específica ou um valor antes de continuar. São utilizados para implementar o padrão produtor-consumidor.
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() // Espera até haver espaço livre } items.append(item) print("Adicionado: \(item), Buffer atual: \(items)") condition.signal() // Sinaliza threads que aguardam } func removeItem() -> Int { condition.lock() defer { condition.unlock() } while items.isEmpty { condition.wait() // Espera até haver um elemento } let item = items.removeFirst() print("Removido: \(item), Buffer atual: \(items)") condition.signal() // Sinaliza threads que aguardam espaço return item } } -
Grand Central Dispatch (GCD):
-
Filas seriais: Executam tarefas sequencialmente, uma de cada vez na mesma thread, garantindo a atomicidade do acesso aos recursos.
-
Filas concorrentes com flags Barrier: Permitem executar leituras em paralelo, mas operações de escrita (write) são feitas de forma exclusiva. Isto implementa um modelo de bloqueio leitura-escrita.
import Foundation class ThreadSafeArray<Element> { private var array: [Element] = [] private let queue = DispatchQueue(label: "com.mycompany.threadsafearray", attributes: .concurrent) // Fila concorrente func append(_ newElement: Element) { queue.async(flags: .barrier) { // Barreira para escrita self.array.append(newElement) } } func get(at index: Int) -> Element? { var result: Element? queue.sync { // Sincronização para leitura (ou async, se a ordem não for importante) if index < self.array.count { result = self.array[index] } } return result } }
-
-
Operações de Contexto do Core Data: Cada
NSManagedObjectContexttem o seu próprio thread serial privado. O acesso a objetos e ao contexto deve ser feito apenas a partir desse thread.import CoreData // Exemplo de salvamento no contexto do Core Data let context = // Contexto obtido context.perform { // Execução no thread privado do contexto // Trabalhar com objetos do Core Data aqui do { try context.save() } catch { // Tratamento de erro } }
A escolha do método depende da tarefa específica e dos requisitos de desempenho. GCD com filas seriais ou filas concorrentes com barreiras costuma ser a abordagem mais preferida devido à sua eficiência e facilidade de uso. Para o Core Data, sempre utilize os métodos perform ou performAndWait.