Sobes.tech
Middle

O que é uma condição de corrida (race condition)?

sobes.tech IA

Resposta da IA

A condição de corrida ocorre quando dois ou mais threads ou processos acedem simultaneamente a um recurso partilhado (por exemplo, uma variável, um ficheiro, uma base de dados) e o resultado da operação depende da ordem em que esses threads/processos realizam as suas ações. Devido à ordem imprevisível de acesso e modificação de dados, o resultado pode ser incorreto ou inesperado.

Exemplo em iOS: Suponha que temos um contador count, ao qual acedem simultaneamente dois threads para o incrementar.

var count = 0

func incrementCount() {
    // Thread 1 lê count = 5
    // Thread 2 lê count = 5
    let currentCount = count
    // Thread 1 calcula newCount = 6
    // Thread 2 calcula newCount = 6
    let newCount = currentCount + 1
    // Thread 1 escreve count = 6
    // Thread 2 escreve count = 6 (sobre-escrevendo o resultado do Thread 1)
    count = newCount
}

// Se dois threads chamarem incrementCount() simultaneamente,
// o resultado esperado seria count = 2,
// mas devido à condição de corrida, count pode tornar-se 1.

Para evitar condições de corrida, utilizam-se mecanismos de sincronização:

  • Lock/Mutex: Bloqueia o acesso ao recurso enquanto o thread o utiliza.
    import Foundation
    
    let lock = NSLock()
    var count = 0
    
    func incrementCount() {
        lock.lock() // Adquire o bloqueio
        let currentCount = count
        let newCount = currentCount + 1
        count = newCount
        lock.unlock() // Liberta o bloqueio
    }
    
  • Filas (Queues): Utilização de filas GCD sequenciais.
    import Foundation
    
    let serialQueue = DispatchQueue(label: "com.myapp.counterQueue")
    var count = 0
    
    func incrementCount() {
        serialQueue.async {
            let currentCount = count
            let newCount = currentCount + 1
            count = newCount
        }
    }
    
  • Operações atómicas: Operações indivisíveis ao nível das instruções do processador. Em Swift, pode-se usar OSAtomic... (obsoleto) ou Atomic de @_implementationOnly import Atomics (experimental), ou abordagens baseadas em bloqueios/filas.

A escolha do método de sincronização depende do cenário específico e dos requisitos de desempenho. É importante entender que a sincronização pode introduzir carga adicional e potencialmente levar a bloqueios mútuos (deadlocks) se não for implementada corretamente.