Sobes.tech
Middle

Quais são as formas de garantir a segurança dos threads em aplicações iOS?

sobes.tech IA

Resposta da IA

Para garantir a segurança de threads no iOS, utilizam-se os seguintes mecanismos principais:

  1. Mutex (exclusão mútua):

    • pthread_mutex_t: API de baixo nível em C do POSIX.
    • NSLock: envoltório orientado a objetos sobre primitivas de bloqueio, parte do Foundation.
    • NSRecursiveLock: Permite bloqueio recursivo pelo mesmo thread.
    import Foundation
    
    let lock = NSLock()
    var recursoCompartilhado = 0
    
    func modificarRecursoCompartilhado() {
        lock.lock()
        // Seção crítica
        recursoCompartilhado += 1
        lock.unlock()
    }
    
  2. Spin Lock:

    • OSSpinLock: obsoleto e não recomendado devido a problemas de prioridades e consumo excessivo de CPU em esperas prolongadas. Substituído por os_unfair_lock.
    • os_unfair_lock: substituição mais eficiente de OSSpinLock, parte do os.xnu.
    import os.lock
    
    var unfairLock = os_unfair_lock()
    var outroRecurso = 0
    
    func atualizarRecurso() {
        os_unfair_lock_lock(&unfairLock)
        // Seção crítica
        outroRecurso += 1
        os_unfair_lock_unlock(&unfairLock)
    }
    
  3. Semáforo:

    • DispatchSemaphore: semáforo do Grand Central Dispatch (GCD). Controla o acesso a um recurso por meio de um contador.
    import Foundation
    
    let semaphore = DispatchSemaphore(value: 1) // Contador = 1 (equivalente a um mutex)
    var recursoLimitado = 0
    
    func acessarRecursoLimitado() {
        semaphore.wait() // Diminui o contador, bloqueia se <= 0
        // Seção crítica
        recursoLimitado += 1
        semaphore.signal() // Aumenta o contador
    }
    
  4. Fila de execução concorrente com tarefas de barreira:

    • Uso de fila concorrente no GCD para leitura e escrita. A leitura é feita em paralelo (async), a escrita de forma exclusiva (sync(flags: .barrier)).
    import Foundation
    
    let filaConcorrente = DispatchQueue(label: "com.exemplo.filaConcorrente", attributes: .concurrent)
    var arrayProtegido: [Int] = []
    
    func adicionarItem(_ item: Int) {
        filaConcorrente.sync(flags: .barrier) {
            // Tarefa com a flag .barrier executada de forma exclusiva
            arrayProtegido.append(item)
        }
    }
    
    func lerArray() -> [Int] {
        var copiaArray: [Int] = []
        filaConcorrente.sync {
            // Tarefa síncrona normal (leitura) pode ser paralela a outras leituras
            copiaArray = arrayProtegido
        }
        return copiaArray
    }
    
  5. Operações atômicas:

    • Operações de baixo nível que garantem atomicidade (executadas como uma única unidade, sem interrupções). Usadas para tipos de dados simples (inteiros, ponteiros).
    import Darwin // Para algumas funções atômicas, como OSAtomic...
    
    var contadorAtômico: Int32 = 0
    
    func incrementarContadorAtomic() -> Int32 {
        // Exemplo de operação atômica
        return OSAtomicIncrement32(&contadorAtômico)
    }
    
    • Em Swift, há tipos experimentais Atomic<Value> em swift-atomics.
  6. Sanitizador de Thread:

    • Ferramenta de depuração que detecta condições de corrida (data races) durante a execução do aplicativo. É ativada no esquema de compilação do Xcode (Edit Scheme -> Run -> Diagnostics -> Thread Sanitizer).

A escolha do mecanismo depende da tarefa:

  • Acesso exclusivo simples: NSLock, DispatchSemaphore(value: 1), os_unfair_lock.
  • Bloqueio recursivo: NSRecursiveLock.
  • Leitura/escrita: fila concorrente com .barrier.
  • Restrição de acesso (piscina de recursos): DispatchSemaphore (com contador > 1).
  • Contadores/falgs simples: operações atômicas (se aplicável e se for necessário máximo desempenho, com cautela).

Evitar:

  • OSSpinLock.
  • Uso de bloqueios globais sem necessidade.
  • Otimização prematura com primitivas de baixo nível sem compreensão de seu funcionamento.