Sobes.tech
Middle

Qu'est-ce qu'une condition de course (race condition) ?

sobes.tech IA

Réponse de l'IA

L’état de course se produit lorsque deux ou plusieurs threads ou processus accèdent simultanément à une ressource partagée (par exemple, une variable, un fichier, une base de données) et que le résultat de l’opération dépend de l’ordre dans lequel ces threads/processus effectuent leurs actions. En raison de l’ordre imprévisible d’accès et de modification des données, le résultat peut être incorrect ou inattendu.

Exemple en iOS : Supposons que nous ayons un compteur count, auquel accèdent simultanément deux threads pour l’incrémenter.

var count = 0

func incrementCount() {
    // Thread 1 lit count = 5
    // Thread 2 lit count = 5
    let currentCount = count
    // Thread 1 calcule newCount = 6
    // Thread 2 calcule newCount = 6
    let newCount = currentCount + 1
    // Thread 1 écrit count = 6
    // Thread 2 écrit count = 6 (écrasant le résultat du Thread 1)
    count = newCount
}

// Si deux threads appellent incrementCount() simultanément,
// le résultat attendu serait count = 2,
// mais en raison de la condition de course, count peut devenir 1.

Pour éviter les conditions de course, on utilise des mécanismes de synchronisation :

  • Lock/Mutex : Bloque l’accès à la ressource pendant que le thread l’utilise.
    import Foundation
    
    let lock = NSLock()
    var count = 0
    
    func incrementCount() {
        lock.lock() // Acquisition du verrou
        let currentCount = count
        let newCount = currentCount + 1
        count = newCount
        lock.unlock() // Libération du verrou
    }
    
  • Queues (File d’attente) : Utilisation de queues GCD séquentielles.
    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
        }
    }
    
  • Opérations atomiques : Opérations indivisibles au niveau des instructions du processeur. En Swift, on peut utiliser OSAtomic... (obsolète) ou Atomic de @_implementationOnly import Atomics (expérimental), ou des approches basées sur des verrouillages/queues.

Le choix de la méthode de synchronisation dépend du scénario spécifique et des exigences de performance. Il est important de comprendre que la synchronisation peut ajouter une charge supplémentaire et potentiellement entraîner des blocages mutuels (deadlocks) si elle est mal implémentée.