Назад к вопросам
Middle
103
questionbank

Что такое состояние гонки (race condition)?

Sobes Copilot

Получайте ответы в реальном времени

Ответ от нейросети

sobes.tech AI

Состояние гонки возникает, когда два или более потока или процессов одновременно обращаются к общему ресурсу (например, переменной, файлу, базе данных) и результат операции зависит от того, в каком порядке эти потоки/процессы выполнят свои действия. Из-за непредсказуемого порядка доступа и изменения данных, результат может быть некорректным или неожиданным.

Пример в iOS: Предположим, у нас есть счетчик count, к которому одновременно обращаются два потока для инкрементации.

var count = 0

func incrementCount() {
    // Поток 1 считывает count = 5
    // Поток 2 считывает count = 5
    let currentCount = count
    // Поток 1 вычисляeт newCount = 6
    // Поток 2 вычисляeт newCount = 6
    let newCount = currentCount + 1
    // Поток 1 записывает count = 6
    // Поток 2 записывает count = 6 (перезаписывая результат Потока 1)
    count = newCount
}

// Если два потока вызовут incrementCount() одновременно,
// ожидаемый результат будет count = 2,
// но из-за состояния гонки, count может стать равным 1.

Для предотвращения состояний гонки применяются механизмы синхронизации:

  • Lock/Mutex: Блокируют доступ к ресурсу, пока поток его использует.
    import Foundation
    
    let lock = NSLock()
    var count = 0
    
    func incrementCount() {
        lock.lock() // Захватываем блокировку
        let currentCount = count
        let newCount = currentCount + 1
        count = newCount
        lock.unlock() // Освобождаем блокировку
    }
    
  • Очереди (Queues): Использование последовательных очередей GCD.
    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
        }
    }
    
  • Атомарные операции: Неразрывные операции на уровне инструкций процессора. В Swift можно использовать OSAtomic... (устарело) или Atomic из @_implementationOnly import Atomics (экспериментально), или же подходы на основе блокировок/очередей.

Выбор метода синхронизации зависит от конкретного сценария и требований к производительности. Важно понимать, что синхронизация может вносить дополнительную нагрузку и потенциально приводить к взаимным блокировкам (deadlocks), если реализована некорректно.