Какие примитивы синхронизации доступны в iOS (Swift)?

«Какие примитивы синхронизации доступны в iOS (Swift)?» — вопрос из категории Многопоточность, который задают на 23% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Для безопасного доступа к общим ресурсам из нескольких потоков в iOS используется ряд примитивов.

Основные примитивы (от простых к сложным):

  1. NSLock / os_unfair_lock

    • Простая взаимная блокировка (mutex). NSLock — высокоуровневый, os_unfair_lock — низкоуровневый и более эффективный.
      
      let lock = NSLock()
      var sharedValue = 0

    func increment() { lock.lock() defer { lock.unlock() } // Гарантирует разблокировку sharedValue += 1 }

  2. DispatchSemaphore

    • Ограничивает количество потоков, одновременно имеющих доступ к ресурсу (часто значение = 1 для мьютекса).
      let semaphore = DispatchSemaphore(value: 1)
      semaphore.wait() // Уменьшает счетчик
      // Критическая секция
      semaphore.signal() // Увеличивает счетчик
  3. DispatchQueue с барьером (Barrier)

    • Идеально для чтения/записи. Позволяет множественное чтение, но запись происходит эксклюзивно.
      
      let concurrentQueue = DispatchQueue(label: "com.queue", attributes: .concurrent)
      var internalArray = [Int]()

    func write(_ value: Int) { concurrentQueue.async(flags: .barrier) { internalArray.append(value) // Эксклюзивная запись } } func read() -> [Int] { concurrentQueue.sync { // Одновременное чтение return internalArray } }

  4. NSRecursiveLock

    • Разновидность NSLock, которую один поток может захватывать несколько раз. Нужен для рекурсивных функций.
  5. Actor (Swift 5.5+)

    • Современный, безопасный подход. Компилятор гарантирует изоляцию: доступ к свойствам актора возможен только асинхронно.
      actor Counter {
      private var value = 0
      func increment() { value += 1 }
      func getValue() -> Int { value }
      }
      // Использование:
      let counter = Counter()
      Task {
      await counter.increment()
      let currentValue = await counter.getValue()
      }

Рекомендации по выбору:

  • Для простых критических секций: NSLock или os_unfair_lock.
  • Для паттерна читатель-писатель: DispatchQueue с барьером.
  • Для нового кода (Swift Concurrency): Используйте actor.
  • Избегайте устаревших @synchronized (Objective-C) и OSAtomic операций.