O que sabes sobre o problema do leitor e do escritor?
sobes.tech IA
Resposta da IA
O problema do leitor e do escritor é uma tarefa clássica de sincronização de acesso a dados partilhados em ambientes multithread.
A essência do problema:
- Múltiplos threads (leitores) leem dados simultaneamente.
- Múltiplos threads (escritores) modificam dados simultaneamente.
Requisitos:
- Os leitores podem ler sem restrições simultaneamente.
- Os escritores têm acesso exclusivo aos dados (enquanto um escritor trabalha, ninguém mais, nem leitor nem escritor, pode aceder).
- Apenas um escritor pode trabalhar de cada vez.
- Se um escritor espera acesso, os novos leitores não devem aceder até que o escritor termine. Esta regra evita a "fome" dos escritores.
Soluções em iOS:
-
NSLock: Mecanismo simples, mas não ótimo para esta tarefa, pois bloqueia tanto leitura como escrita.
-
NSRecursiveLock: Permite que o mesmo thread obtenha o bloqueio várias vezes. Não aplicável.
-
NSCondition: Mecanismo mais flexível que permite aos threads esperar certas condições. Pode ser usado para implementar lógica de leitores/escritores, mas requer gestão manual de bloqueios e condições.
-
Serial Dispatch Queue (GCD): Criação de uma fila sequencial para todas as operações de leitura e escrita. As escritas são feitas de forma síncrona, a leitura pode ser feita de forma assíncrona, mas apenas após as operações anteriores terminarem. É uma solução simples, mas não ótima em desempenho para leitura, pois a leitura não pode ser paralela.
let readWriteQueue = DispatchQueue(label: "com.example.readwrite", attributes: .concurrent) func readData() { readWriteQueue.async { // Lógica de leitura de dados print("Lendo dados...") } } func writeData() { readWriteQueue.sync(flags: .barrier) { // Lógica de escrita de dados print("Escrevendo dados...") } } -
Concurrent Dispatch Queue com Barrier (GCD): Solução ótima na maioria dos casos. Usa uma fila concorrente para executar operações de leitura em paralelo. Para operações de escrita, usa a bandeira
.barrier. Quando uma operação com essa bandeira é colocada na fila, ela espera que todas as operações anteriores terminem, depois é executada, e enquanto ela está a ser executada, nenhuma outra operação (leitura ou escrita) pode ocorrer. Após a sua conclusão, a fila volta a ser concorrente. Assim, a leitura é paralela, e a escrita é exclusiva.let readWriteQueue = DispatchQueue(label: "com.example.readwrite", attributes: .concurrent) func readData() { readWriteQueue.async { // Lógica de leitura de dados print("Lendo dados...") } } func writeData() { readWriteQueue.sync(flags: .barrier) { // Lógica de escrita de dados print("Escrevendo dados...") } }É o mesmo código que para a fila serial, mas o atributo
.concurrentfaz a fila ser concorrente. A bandeira.barrierfaz a fila comportar-se como sequencial apenas para essa operação específica. -
os_unfair_lock (iOS 10+): Bloqueio de baixo nível, mais eficiente que
NSLock. Pode ser usado para implementar lógica mais complexa de leitores/escritores manualmente, se for necessário máximo desempenho ou comportamento específico. Requer uso cuidadoso.
A escolha da solução depende dos requisitos específicos da aplicação, da frequência de operações de leitura e escrita, e do nível de desempenho necessário. Na maioria dos casos, usar uma fila de despacho concorrente com a bandeira .barrier é a abordagem mais equilibrada e recomendada em iOS.