Skip to content

03 - Concurrency 深度

目录

  1. 线程模型与调度策略
  2. 死锁:产生条件与避免方案
  3. 内存屏障与原子操作
  4. 并发设计模式
  5. 面试题汇总

1. 线程模型与调度策略

iOS 线程模型:
┌────────────────────────────────────────────────────────────────────────┐
│                                                                      │
│  线程类型:                                                           │
│  ┌───────────────────────────────────────────────────────────────┐  │
│  │  主线程(Main Thread)                                         │  │
│  │  • UI 更新唯一线程                                             │  │
│  │  • RunLoop 驱动事件循环                                       │  │
│  │  • 默认优先级(userInteractive)                               │  │
│  │  • 不能阻塞                                                   │  │
│  │  • 所有 UI 操作必须在此线程                                     │  │
│  │                                                                  │  │
│  │  后台线程(Background Thread)                                   │  │
│  │  • 网络请求、数据处理                                           │  │
│  │  • 优先级可配置(QoS)                                          │  │
│  │  • 数量受系统限制(约 64 个)                                  │  │
│  │  • 通过 DispatchQueue 创建                                      │  │
│  │                                                                  │  │
│  │  线程池(Thread Pool)                                            │  │
│  │  • GCD 自动管理线程池                                           │  │
│  │  • 自动线程复用                                                  │  │
│  │  • 线程数量根据负载动态调整                                     │  │
│  │  • 空闲线程会自动回收                                            │  │
│  └─────────────────────────────────────────────────────────────────┘  │
│                                                                      │
│  调度策略:                                                          │
│  ┌───────────────────────────────────────────────────────────────┐  │
│  │  优先级抢占调度(Preemptive Scheduling)                         │  │
│  │  • 高优先级任务优先执行                                         │  │
│  │  • 低优先级任务可能被抢占                                       │  │
│  │  • iOS 系统使用 CFScheduler                                    │  │
│  │                                                                  │  │
│  │  时间片轮转调度(Round-Robin)                                    │  │
│  │  • 每个线程分配固定时间片                                       │  │
│  │  • 时间片用完 → 切换到下一个线程                                │  │
│  │  • 适用于同等优先级线程                                         │  │
│  │                                                                  │  │
│  │  实时调度(Real-Time Scheduling)                               │  │
│  │  • 最高优先级                                                   │  │
│  │  • 保证响应时间                                               │  │
│  │  • 仅用于系统关键任务                                           │  │
│  └──────────────────────────────────────────────────────────────┘  │
│                                                                      │
│  QoS 映射:                                                          │
│  ┌──────────────────────────────────────────────────────────────┐  │
│  │  UserInteractive → 最高优先级(保证 UI 流畅)                   │  │
│  │  UserInitiated → 高优先级(用户发起的任务)                      │  │
│  │  Default → 默认优先级(系统推荐)                               │  │
│  │  Utility → 低优先级(耗时任务)                                 │  │
│  │  Background → 最低优先级(后台刷新等)                          │  │
│  └──────────────────────────────────────────────────────────────┘  │
│                                                                      │
│  线程性能分析:                                                     │
│  ┌──────────────────────────────────────────────────────────────┐  │
│  │  • 线程创建:约 1MB 内存(栈空间)                              │  │
│  │  • 线程切换:约 10-50μs                                      │  │
│  │  • GCD 线程池:复用现有线程(零创建开销)                      │  │
│  │  • async/await 协程:约 4KB 内存                              │  │
│  │  • 大量线程 → 内存压力/上下文切换开销                          │  │
│  └──────────────────────────────────────────────────────────────┘  │
│                                                                      │
└────────────────────────────────────────────────────────────────────────────────┐
*/

2. 死锁:产生条件与避免方案

死锁分析:
┌──────────────────────────────────────────────────────────────┐
│                                                                      │
│  死锁的四个必要条件(Coffman Conditions):                            │
│  ┌──────────────────────────────────────────────────────────┐     │
│  │  1. 互斥(Mutual Exclusion)— 资源只能被一个线程占用         │     │
│  │  2. 持有并等待(Hold and Wait)— 持有资源时等待其他资源       │     │
│  │  3. 不可抢占(No Preemption)— 资源不能被强制回收             │     │
│  │  4. 循环等待(Circular Wait)— 线程形成等待环                │     │
│  │                                                              │     │
│  │  死锁检测:                                                   │     │
│  │  • 资源分配图(Resource Allocation Graph)                   │     │
│  │  • 如果图中存在环 → 可能死锁                                 │     │
│  └──────────────────────────────────────────────────────────┘     │
│                                                                      │
│  常见的 iOS 死锁场景:                                              │
│  ┌──────────────────────────────────────────────────────────┐     │
│  │  场景 1:sync 到当前队列                                    │     │
│  │  ┌──────────────────────────────────────┐                 │     │
│  │  │  主线程:                              │                 │     │
│  │  │  1. 执行 sync 到主队列                 │                 │     │
│  │  │  2. 等待主队列任务完成                 │                 │     │
│  │  │  3. 主队列任务需要主线程执行            │                 │     │
│  │  │  4. 死锁!                           │                 │     │
│  │  └──────────────────────────────────────┘                 │     │
│  │                                                              │     │
│  │  场景 2:线程 A 锁 1 → 锁 2 / 线程 B 锁 2 → 锁 1            │     │
│  │  ┌──────────────────────────────────────┐                 │     │
│  │  │  Thread A: lock(1) → lock(2)           │                 │     │
│  │  │  Thread B: lock(2) → lock(1)           │                 │     │
│  │  │  → 互相等待 → 死锁                     │                 │     │
│  │  └──────────────────────────────────────┘                 │     │
│  │                                                              │     │
│  │  场景 3:DispatchGroup 等待超时                              │     │
│  │  ┌──────────────────────────────────────┐                 │     │
│  │  │  group.wait(timeout: .now() + 5)     │                 │     │
│  │  │  → 如果任务未 enter/leave 平衡        │                 │     │
│  │  │  → 永久等待 → 逻辑死锁                 │                 │     │
│  │  └──────────────────────────────────────┘                 │     │
│  │                                                              │     │
│  │  场景 4:主线程同步调用异步任务                              │     │
│  │  ┌──────────────────────────────────────┐                 │     │
│  │  │  主线程 sync 到主队列                 │                 │     │
│  │  │  或 async 到主队列 同步等待           │                 │     │
│  │  └──────────────────────────────────────┘                 │     │
│  └──────────────────────────────────────────────────────────┘     │
│                                                                      │
│  避免死锁的策略:                                                   │
│  ┌──────────────────────────────────────────────────────────┐     │
│  │  1. 始终使用 async 而非 sync                               │     │
│  │  2. 统一锁的顺序(所有线程按相同顺序获取锁)                  │     │
│  │  3. 使用 DispatchQueue 代替原生锁                         │     │
│  │  4. 使用 DispatchGroup 确保 enter/leave 平衡               │     │
│  │  5. 设置超时(group.wait(timeout:))                        │     │
│  │  6. 使用 actor 隔离(避免手动锁)                          │     │
│  │  7. 使用 os_unfair_lock(可尝试获取,不会阻塞等待)         │     │
│  │  8. 避免嵌套锁                                              │     │
│  │  9. 使用 dispatch_semaphore 而非 manual lock               │     │
│  │  10. 使用 Thread Sanitizer 检测死锁                         │     │
│  │                                                              │     │
│  │  死锁检测工具:                                               │     │
│  │  • Thread Sanitizer(TSan)                                │     │
│  │  • Instruments → Thread Debugger                          │     │
│  │  • os_log 日志分析                                           │     │
│  │  • Xcode debugger                                            │     │
│  └──────────────────────────────────────────────────────────┘     │
│                                                                      │
│  ⚠️ 最佳实践:                                                     │
│  • 永远不要在主线程同步调用主队列任务                                │
│  • 使用 async/await 替代 sync                                     │
│  • 使用 actor 代替手动锁                                          │
│  • 使用 DispatchGroup + timeout 防止永久等待                        │
│  • 所有锁必须成对获取/释放(使用 defer)                            │
│                                                                      │
└──────────────────────────────────────────────────────────┘
*/

3. 内存屏障与原子操作

内存屏障与原子操作:
┌─────────────────────────────────────────────────────────┐
│                                                                      │
│  内存屏障(Memory Barrier):                                      │
│  ┌────────────────────────────────────────────────────────┐     │
│  │  • 保证内存操作的顺序性                                   │     │
│  │  • 防止编译器/CPU 重排序                                  │     │
│  │  • 在多核 CPU 中至关重要                                   │     │
│  │  • iOS 中使用 os_atomic_* API                            │     │
│  │                                                            │     │
│  │  内存屏障类型:                                           │     │
│  │  • acquire(获取)— 屏障后的读不能重排序到屏障前           │     │
│  │  • release(释放)— 屏障前的写不能重排序到屏障后           │     │
│  │  • acquire-release — 组合屏障                             │     │
│  │  • seq_cst(顺序一致性)— 最强的内存屏障                   │     │
│  │                                                            │     │
│  │  使用场景:                                                │     │
│  │  • 多线程共享变量的读写                                    │     │
│  │  • 实现无锁数据结构                                         │     │
│  │  • 实现同步原语(如 spin lock)                           │     │
│  └────────────────────────────────────────────────────────┘     │
│                                                                      │
│  原子操作(Atomic Operations):                                    │
│  ┌────────────────────────────────────────────────────────┐     │
│  │  • 不可分割的操作(要么全部执行,要么不执行)                │     │
│  │  • 不需要锁                                                  │     │
│  │  • 比锁更高效                                                  │     │
│  │  • iOS 使用 os_unfair_lock 和 os_atomic_* API              │     │
│  │                                                            │     │
│  │  os_unfair_lock(推荐 ✅)                                   │     │
│  │  ┌───────────────────────────────────────────────────┐    │     │
│  │  │  let lock = os_unfair_lock()                       │    │     │
│  │  │  os_unfair_lock_lock(&lock)                        │    │     │
│  │  │  // 临界区                                          │    │     │
│  │  │  os_unfair_lock_unlock(&lock)                      │    │     │
│  │  │                                                   │    │     │
│  │  │  // 与 NSLock 对比                                │    │     │
│  │  │  • os_unfair_lock:C API,更高效                    │    │     │
│  │  │  • NSLock:ObjC 封装,更易用                        │    │     │
│  │  │  • 性能:os_unfair_lock > NSLock                   │    │     │
│  │  └───────────────────────────────────────────────────┘    │     │
│  │                                                            │     │
│  │  os_atomic(原子操作 API)                               │     │
│  │  ┌───────────────────────────────────────────────────┐    │     │
│  │  │  os_atomic_increment(&counter, memory_order_relaxed)  │
│  │  │  os_atomic_decrement(&counter, memory_order_relaxed)  │
│  │  │  os_atomic_load(&var, memory_order_relaxed)        │    │     │
│  │  │  os_atomic_store(&var, newValue, memory_order_relaxed)  │
│  │  │                                                   │    │     │
│  │  │  memory_order 类型:                              │    │     │
│  │  │  • relaxed:无顺序保证(最高性能)                 │    │     │
│  │  │  • acquire:获取语义                               │    │     │
│  │  │  • release:释放语义                               │    │     │
│  │  │  • acquire-release:组合                           │    │     │
│  │  │  • seq_cst:顺序一致(最强)                       │    │     │
│  │  └───────────────────────────────────────────────────┘    │     │
│  └────────────────────────────────────────────────────────┘     │
│                                                                      │
│  无锁编程(Lock-Free Programming):                                │
│  ┌────────────────────────────────────────────────────────┐     │
│  │  优点:                                                   │     │
│  │  • 无锁竞争 → 更高性能                                    │     │
│  │  • 不会死锁                                              │     │
│  │  • 线程安全                                               │     │
│  │                                                            │     │
│  │  缺点:                                                   │     │
│  │  • 实现复杂                                                │     │
│  │  • 容易引入微妙 bug                                        │     │
│  │  • 不适合所有场景                                          │     │
│  │                                                            │     │
│  │  CAS(Compare-And-Swap)原子操作:                       │     │
│  │  ┌───────────────────────────────────────────────────┐    │     │
│  │  │  1. 读取旧值                                       │    │     │
│  │  │  2. 比较旧值与期望值                                │    │     │
│  │  │  3. 相等 → 更新为新值                              │    │     │
│  │  │  4. 不等 → 重试                                    │    │     │
│  │  │                                                   │    │     │
│  │  │  应用场景:                                       │    │     │
│  │  │  • 无锁队列                                       │    │     │
│  │  │  • 引用计数                                      │    │     │
│  │  │  • 计数器                                         │    │     │
│  │  └───────────────────────────────────────────────────┘    │     │
│  └────────────────────────────────────────────────────────┘     │
│                                                                      │
│  性能分析:                                                       │
│  ┌────────────────────────────────────────────────────────┐     │
│  │  • NSLock:约 100-200ns                                │     │
│  │  • os_unfair_lock:约 50-100ns                         │     │
│  │  • os_atomic:约 10-50ns                              │     │
│  │  • CAS:约 10-30ns                                   │     │
│  │  • 锁 vs 原子操作:原子操作更快(无内核调用)           │     │
│  └────────────────────────────────────────────────────────┘     │
│                                                                      │
└─────────────────────────────────────────────────────────┘
*/

4. 并发设计模式

并发设计模式:
┌─────────────────────────────────────────────────────────┐
│                                                                      │
│  1. 生产者-消费者模式                                               │
│  ┌────────────────────────────────────────────────────────┐     │
│  │  actor Queue {                                            │     │
│  │      private var items: [Data] = []                      │     │
│  │      private let maxItems = 100                          │     │
│  │                                                            │     │
│  │      func enqueue(item: Data) async {                   │     │
│  │          while items.count >= maxItems {                 │     │
│  │              await Task.yield()  // 等待空间            │     │
│  │          }                                                │     │
│  │          items.append(item)                               │     │
│  │      }                                                   │     │
│  │                                                            │     │
│  │      func dequeue() async -> Data? {                    │     │
│  │          guard !items.isEmpty else {                      │     │
│  │              await Task.yield()  // 等待数据            │     │
│  │              return items.isEmpty ? nil : dequeue()       │     │
│  │          }                                                │     │
│  │          return items.removeFirst()                       │     │
│  │      }                                                   │     │
│  │  }                                                        │     │
│  │                                                            │     │
│  │  使用:                                                   │     │
│  │  Task { for item in source { await queue.enqueue(item: item) } }  │
│  │  Task { while let data = await queue.dequeue() { process(data) } }  │
│  └────────────────────────────────────────────────────────┘     │
│                                                                      │
│  2. 工作线程池模式                                                  │
│  ┌────────────────────────────────────────────────────────┐     │
│  │  actor ThreadPool {                                        │     │
│  │      private var workers: [Int: Task<Void, Never>] = []   │     │
│  │      private var taskQueue: [() async -> Void] = []        │     │
│  │      private let maxWorkers = 8                            │     │
│  │                                                            │     │
│  │      func submit(work: @escaping () async -> Void) {      │     │
│  │          if workers.count < maxWorkers {                   │     │
│  │              let worker = Task {                           │     │
│  │                  while let task = taskQueue.first {        │     │
│  │                      taskQueue.removeFirst()                │     │
│  │                      await task()                           │     │
│  │                  }                                         │     │
│  │              }                                              │     │
│  │              workers[worker.hashValue] = worker             │     │
│  │          } else {                                           │     │
│  │              taskQueue.append(work)                         │     │
│  │          }                                                  │     │
│  │      }                                                     │     │
│  │  }                                                          │     │
│  └────────────────────────────────────────────────────────┘     │
│                                                                      │
│  3. Future/Promise 模式                                              │
│  ┌────────────────────────────────────────────────────────┐     │
│  │  class Promise<T> {                                        │     │
│  │      private var continuation: CheckedContinuation<T, Error>?  │
│  │      private var value: Result<T, Error>?                  │     │
│  │                                                            │     │
│  │      func then(_ handler: @escaping (T) async throws -> R) -> Promise<R> {  │
│  │          return Promise<R> { resolver in                   │     │
│  │              let result = try await value.get()             │     │
│  │              let newValue = try await handler(result)       │     │
│  │              resolver.fulfill(newValue)                     │     │
│  │          }                                                  │     │
│  │      }                                                     │     │
│  │  }                                                          │     │
│  └────────────────────────────────────────────────────────┘     │
│                                                                      │
│  4. Barrier(屏障)模式                                               │
│  ┌────────────────────────────────────────────────────────┐     │
│  │  actor SharedData {                                          │     │
│  │      private var data: [String: Data] = [:]                 │     │
│  │      private var isReady = false                            │     │
│  │                                                            │     │
│  │      func write(key: String, value: Data) async {         │     │
│  │          data[key] = value                                  │     │
│  │          if data.count >= threshold { isReady = true }     │     │
│  │      }                                                     │     │
│  │                                                            │     │
│  │      func waitForAll() async -> [String: Data]? {         │     │
│  │          guard isReady else {                              │     │
│  │              await Task.yield()                             │     │
│  │              return data.count >= threshold ? data : nil    │     │
│  │          }                                                  │     │
│  │          return data                                        │     │
│  │      }                                                     │     │
│  │  }                                                          │     │
│  └────────────────────────────────────────────────────────┘     │
│                                                                      │
│  5. Observer(观察者)模式                                         │
│  ┌───────────────────────────────────────────────────────┐     │
│  │  actor EventEmitter {                                   │     │
│  │      private var listeners: [(Event) -> Void] = []      │     │
│  │                                                            │     │
│  │      func subscribe(listener: @escaping (Event) -> Void) {  │
│  │          listeners.append(listener)                       │     │
│  │      }                                                     │     │
│  │                                                            │     │
│  │      func publish(event: Event) async {                  │     │
│  │          for listener in listeners {                       │     │
│  │              await listener(event)                         │     │
│  │          }                                                  │     │
│  │      }                                                     │     │
│  │  }                                                          │     │
│  └────────────────────────────────────────────────────────┘     │
│                                                                      │
│  模式选择指南:                                                     │
│  ┌──────────────────────────────────────────────────────────┐     │
│  │  场景                    │ 推荐模式                        │   │
│  │  ────                    │ ──────                          │   │
│  │  大量并发数据流           │ 生产者-消费者                    │   │
│  │  固定数量线程池          │ 工作线程池                      │   │
│  │  异步操作链               │ Future/Promise                 │   │
│  │  等待多个任务完成         │ Barrier                        │   │
│  │  事件通知                 │ Observer                       │   │
│  │  共享状态                 │ Actor                          │   │
│  └──────────────────────────────────────────────────────────┘     │
│                                                                      │
└─────────────────────────────────────────────────────────┘
*/

5. 面试题汇总

高频面试题

Q1: 死锁的四个必要条件?

:互斥、持有并等待、不可抢占、循环等待。避免任一条件即可打破死锁。

Q2: iOS 线程模型?

:主线程(UI)、后台线程(网络/数据处理)、线程池(GCD 自动管理)。QoS 决定优先级。

Q3: 内存屏障的作用?

:保证内存操作的顺序性,防止重排序。acquire/release/seq_cst 四种语义。

Q4: 原子操作 vs 锁?

:原子操作更快(无内核调用),不需要锁。OS_unfair_lock > NSLock > 原子操作(CAS)。

Q5: 并发设计模式?

:生产者-消费者、工作线程池、Future/Promise、Barrier、Observer。根据场景选择。


6. 参考资源