聊聊GCD

GCD 详解 - 从入门到精通

📌 快速参考卡片

csharp 复制代码
┌─────────────────────────────────────────────────┐
│  GCD 核心概念                                    │
│  ├─ 队列(Queue):任务容器                       │
│  │   ├─ 串行队列:一次一个任务                   │
│  │   └─ 并行队列:同时多个任务                   │
│  ├─ 执行方式:如何提交任务                       │
│  │   ├─ sync:同步,阻塞等待                     │
│  │   └─ async:异步,立即返回                    │
│  └─ 系统队列:预定义的队列                       │
│      ├─ main:主队列(串行)                     │
│      └─ global:全局队列(并行)                 │
└─────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────┐
│  GCD 高级特性                                    │
│  ├─ DispatchGroup:任务组管理                   │
│  ├─ DispatchSemaphore:信号量控制                │
│  ├─ DispatchBarrier:栅栏任务                   │
│  ├─ DispatchSource:事件源                      │
│  └─ DispatchWorkItem:任务封装                  │
└─────────────────────────────────────────────────┘

一、GCD 常用方法速查表

📋 最常用的 GCD 方法

1. 队列创建和获取
swift 复制代码
// 创建串行队列
let serialQueue = DispatchQueue(label: "com.example.serial")

// 创建并行队列
let concurrentQueue = DispatchQueue(
    label: "com.example.concurrent",
    attributes: .concurrent
)

// 获取主队列
let mainQueue = DispatchQueue.main

// 获取全局队列
let globalQueue = DispatchQueue.global()
let highPriorityQueue = DispatchQueue.global(qos: .userInteractive)
let backgroundQueue = DispatchQueue.global(qos: .background)
2. 任务提交(最常用)
swift 复制代码
// 异步执行(推荐,最常用)
queue.async {
    // 任务代码
    print("异步执行")
}

// 同步执行(谨慎使用)
queue.sync {
    // 任务代码
    print("同步执行")
}

// 异步执行,指定队列
DispatchQueue.main.async {
    // UI 更新
    self.label.text = "更新"
}

// 后台执行
DispatchQueue.global().async {
    // 耗时操作
    processData()
}
3. 延迟执行
swift 复制代码
// 延迟执行(最常用)
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
    print("2秒后执行")
}

// 延迟执行,指定时间
let delay = DispatchTime.now() + DispatchTimeInterval.seconds(3)
DispatchQueue.main.asyncAfter(deadline: delay) {
    print("3秒后执行")
}

// 延迟执行,后台队列
DispatchQueue.global().asyncAfter(deadline: .now() + 1.0) {
    print("1秒后执行")
}
4. 任务组(DispatchGroup)
swift 复制代码
// 创建任务组
let group = DispatchGroup()

// 进入组
group.enter()

// 执行任务
queue.async {
    // 任务代码
    group.leave()  // 离开组
}

// 等待所有任务完成
group.wait()  // 阻塞等待

// 所有任务完成后的回调(最常用)
group.notify(queue: .main) {
    print("所有任务完成")
}
5. 信号量(DispatchSemaphore)
swift 复制代码
// 创建信号量(初始值 = 最大并发数)
let semaphore = DispatchSemaphore(value: 3)

// 等待信号量
semaphore.wait()

// 执行任务
queue.async {
    // 任务代码
    semaphore.signal()  // 释放信号量
}
6. 栅栏任务(DispatchBarrier)
swift 复制代码
// 栅栏任务(在并行队列中使用)
concurrentQueue.async(flags: .barrier) {
    // 这个任务会等待前面的任务完成
    // 执行时阻塞其他任务
    print("栅栏任务")
}
7. 一次性执行(DispatchOnce)
swift 复制代码
// Swift 中推荐使用静态属性实现单例
class MyClass {
    static let shared = MyClass()
    private init() {}
}

// Objective-C 中使用 dispatch_once
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    // 只执行一次的代码
});
8. 任务封装(DispatchWorkItem)
swift 复制代码
// 创建任务
let workItem = DispatchWorkItem {
    print("执行任务")
}

// 执行任务
queue.async(execute: workItem)

// 等待任务完成
workItem.wait()

// 取消任务
workItem.cancel()

// 任务完成通知
workItem.notify(queue: .main) {
    print("任务完成")
}
9. 定时器(DispatchSourceTimer)
swift 复制代码
// 创建定时器
let timer = DispatchSource.makeTimerSource(queue: .global())

// 设置定时器(立即开始,每秒重复)
timer.schedule(deadline: .now(), repeating: 1.0)

// 设置事件处理
timer.setEventHandler {
    print("定时器触发")
}

// 启动定时器
timer.resume()

// 取消定时器
timer.cancel()

🎯 最常用的 10 个方法(按使用频率)

排名 方法 使用场景 代码示例
1 DispatchQueue.main.async UI 更新 DispatchQueue.main.async { self.label.text = "更新" }
2 DispatchQueue.global().async 后台任务 DispatchQueue.global().async { processData() }
3 DispatchQueue.main.asyncAfter 延迟执行 DispatchQueue.main.asyncAfter(deadline: .now() + 2) { }
4 DispatchGroup.notify 等待多个任务 group.notify(queue: .main) { }
5 DispatchQueue(label:) 创建队列 let queue = DispatchQueue(label: "com.example")
6 DispatchSemaphore 控制并发 let semaphore = DispatchSemaphore(value: 3)
7 DispatchGroup.enter/leave 任务组管理 group.enter() ... group.leave()
8 queue.async(flags: .barrier) 读写锁 queue.async(flags: .barrier) { }
9 DispatchWorkItem 任务封装 let workItem = DispatchWorkItem { }
10 DispatchSource.makeTimerSource 定时器 let timer = DispatchSource.makeTimerSource()

💡 常用方法组合模式

模式1:后台处理 + 主线程更新(最常用)
swift 复制代码
DispatchQueue.global().async {
    // 后台处理
    let data = processData()
    
    DispatchQueue.main.async {
        // 主线程更新 UI
        self.updateUI(with: data)
    }
}
模式2:多个任务完成后更新
swift 复制代码
let group = DispatchGroup()

group.enter()
fetchData1 { group.leave() }

group.enter()
fetchData2 { group.leave() }

group.notify(queue: .main) {
    // 所有任务完成,更新 UI
    self.updateUI()
}
模式3:控制并发数量
swift 复制代码
let semaphore = DispatchSemaphore(value: 3)

for url in urls {
    semaphore.wait()
    DispatchQueue.global().async {
        download(url) {
            semaphore.signal()
        }
    }
}
模式4:延迟执行
swift 复制代码
// 延迟 2 秒执行
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
    print("2秒后执行")
}
模式5:读写锁
swift 复制代码
let queue = DispatchQueue(label: "cache", attributes: .concurrent)

// 读操作:并发执行
func read() -> Data? {
    return queue.sync { cache[key] }
}

// 写操作:栅栏任务
func write(_ data: Data) {
    queue.async(flags: .barrier) {
        cache[key] = data
    }
}

🔧 实用工具方法封装

1. 延迟执行工具方法
swift 复制代码
extension DispatchQueue {
    static func delay(_ seconds: Double, completion: @escaping () -> Void) {
        DispatchQueue.main.asyncAfter(deadline: .now() + seconds) {
            completion()
        }
    }
}

// 使用
DispatchQueue.delay(2.0) {
    print("2秒后执行")
}
2. 后台执行 + 主线程回调
swift 复制代码
extension DispatchQueue {
    static func background(_ work: @escaping () -> Void, 
                          completion: @escaping () -> Void) {
        DispatchQueue.global().async {
            work()
            DispatchQueue.main.async {
                completion()
            }
        }
    }
}

// 使用
DispatchQueue.background({
    // 后台处理
    processData()
}) {
    // 主线程回调
    self.updateUI()
}
3. 安全的主线程执行
swift 复制代码
extension DispatchQueue {
    static func safeMain(_ work: @escaping () -> Void) {
        if Thread.isMainThread {
            work()
        } else {
            DispatchQueue.main.async {
                work()
            }
        }
    }
}

// 使用:无论当前在哪个线程,都能安全地在主线程执行
DispatchQueue.safeMain {
    self.label.text = "更新"
}
4. 批量任务执行
swift 复制代码
extension DispatchQueue {
    static func executeBatch<T>(
        items: [T],
        maxConcurrent: Int = 3,
        processor: @escaping (T, @escaping () -> Void) -> Void,
        completion: @escaping () -> Void
    ) {
        let group = DispatchGroup()
        let semaphore = DispatchSemaphore(value: maxConcurrent)
        
        for item in items {
            group.enter()
            semaphore.wait()
            
            DispatchQueue.global().async {
                processor(item) {
                    semaphore.signal()
                    group.leave()
                }
            }
        }
        
        group.notify(queue: .main) {
            completion()
        }
    }
}

// 使用
DispatchQueue.executeBatch(
    items: imageURLs,
    maxConcurrent: 3,
    processor: { url, done in
        downloadImage(url: url) {
            done()
        }
    },
    completion: {
        print("所有图片下载完成")
    }
)

📝 常用方法参数说明

DispatchQueue 初始化参数
swift 复制代码
DispatchQueue(
    label: String,                    // 队列标识符(必填)
    qos: DispatchQoS = .default,      // 服务质量优先级
    attributes: DispatchQueue.Attributes = [],  // 队列属性
    autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency = .inherit,  // 自动释放频率
    target: DispatchQueue? = nil      // 目标队列
)

// 常用属性
.attributes = []              // 串行队列(默认)
.attributes = .concurrent     // 并行队列
QoS 优先级说明
swift 复制代码
.userInteractive    // 用户交互(最高优先级)
.userInitiated      // 用户发起
.default            // 默认
.utility            // 实用工具
.background         // 后台(最低优先级)
.unspecified        // 未指定
asyncAfter 参数说明
swift 复制代码
func asyncAfter(
    deadline: DispatchTime,           // 执行时间
    qos: DispatchQoS = .unspecified, // 优先级
    flags: DispatchWorkItemFlags = [], // 标志
    execute work: @escaping () -> Void // 任务
)

// 常用时间计算
.now() + 2.0                    // 2秒后
.now() + .seconds(3)            // 3秒后
.now() + .milliseconds(500)     // 500毫秒后
.now() + .nanoseconds(1000000)   // 1毫秒后

⚠️ 常见错误和注意事项

错误1:主线程同步调用
swift 复制代码
// ❌ 错误:会死锁
DispatchQueue.main.sync {
    print("这行代码永远不会执行")
}

// ✅ 正确
DispatchQueue.main.async {
    print("这行代码会执行")
}
错误2:循环引用
swift 复制代码
// ❌ 错误:循环引用
DispatchQueue.global().async {
    self.processData()  // 捕获了 self
}

// ✅ 正确:使用 weak self
DispatchQueue.global().async { [weak self] in
    self?.processData()
}
错误3:忘记 leave
swift 复制代码
// ❌ 错误:忘记 leave,group.notify 永远不会执行
let group = DispatchGroup()
group.enter()
DispatchQueue.global().async {
    // 忘记 group.leave()
}

// ✅ 正确:确保 enter 和 leave 配对
let group = DispatchGroup()
group.enter()
DispatchQueue.global().async {
    processData()
    group.leave()  // 记得 leave
}
错误4:信号量忘记 signal
swift 复制代码
// ❌ 错误:忘记 signal,其他任务无法执行
let semaphore = DispatchSemaphore(value: 1)
semaphore.wait()
// 忘记 semaphore.signal()

// ✅ 正确:使用 defer 确保释放
let semaphore = DispatchSemaphore(value: 1)
semaphore.wait()
defer { semaphore.signal() }
// 任务代码

二、GCD 基础概念

1. 什么是 GCD?

GCD(Grand Central Dispatch) 是 Apple 提供的多线程编程解决方案,基于 C 语言实现。

核心思想:

  • 将任务添加到队列中
  • 系统自动管理线程的创建和调度
  • 开发者不需要直接管理线程

优势:

  • ✅ 简单易用,API 简洁
  • ✅ 自动管理线程生命周期
  • ✅ 性能优秀,充分利用多核 CPU
  • ✅ 线程安全,减少竞态条件

2. 核心概念

队列(Queue)

队列是任务的容器,按照 FIFO(先进先出)的顺序管理任务。

swift 复制代码
// 队列只是任务的容器,不是线程
let queue = DispatchQueue(label: "com.example.queue")
任务(Task)

需要执行的代码块,通常是闭包。

swift 复制代码
queue.async {
    // 这是任务
    print("执行任务")
}
线程(Thread)

实际执行任务的执行单元,由系统管理。

复制代码
队列 → 任务 → 线程执行

二、队列类型详解

1. 串行队列(Serial Queue)

定义

串行队列一次只执行一个任务,任务按顺序执行。

swift 复制代码
// 创建串行队列
let serialQueue = DispatchQueue(label: "com.example.serial")
特点
  • 顺序执行:任务按添加顺序依次执行
  • 单线程执行:同一时间只有一个任务在执行
  • 线程安全:天然保证任务执行的顺序性
代码示例
swift 复制代码
let serialQueue = DispatchQueue(label: "serial")

serialQueue.async {
    print("任务1开始")
    sleep(2)
    print("任务1结束")
}

serialQueue.async {
    print("任务2开始")
    sleep(1)
    print("任务2结束")
}

// 输出顺序(固定):
// 任务1开始
// 任务1结束
// 任务2开始
// 任务2结束
应用场景
  • UI 更新(主队列)
  • 线程安全的数据访问
  • 需要严格顺序执行的任务
  • 日志记录

2. 并行队列(Concurrent Queue)

定义

并行队列可以同时执行多个任务,充分利用多核 CPU。

swift 复制代码
// 创建并行队列
let concurrentQueue = DispatchQueue(
    label: "com.example.concurrent",
    attributes: .concurrent
)
特点
  • 并发执行:多个任务可以同时在不同线程上执行
  • 性能高:充分利用多核 CPU
  • ⚠️ 完成顺序不确定:任务开始顺序确定,但完成顺序可能不同
代码示例
swift 复制代码
let concurrentQueue = DispatchQueue(
    label: "concurrent",
    attributes: .concurrent
)

concurrentQueue.async {
    print("任务1开始")
    sleep(2)
    print("任务1结束")
}

concurrentQueue.async {
    print("任务2开始")
    sleep(1)
    print("任务2结束")
}

// 可能的输出(不确定):
// 任务1开始
// 任务2开始
// 任务2结束  (1秒后)
// 任务1结束  (2秒后)
应用场景
  • 多图片下载
  • 数据处理
  • 网络请求并发
  • CPU 密集型任务

3. 系统队列

主队列(Main Queue)

主队列是串行队列,所有任务在主线程执行。

swift 复制代码
// 获取主队列
let mainQueue = DispatchQueue.main

// 所有 UI 操作必须在主队列执行
DispatchQueue.main.async {
    self.imageView.image = image
    self.tableView.reloadData()
}

特点:

  • 串行队列
  • 所有任务在主线程执行
  • 用于 UI 更新

⚠️ 注意:

swift 复制代码
// ❌ 死锁:在主线程同步调用主队列
DispatchQueue.main.sync {
    print("这行代码永远不会执行")
}
全局队列(Global Queue)

全局队列是并行队列,系统提供的全局并发队列。

swift 复制代码
// 获取全局队列
let globalQueue = DispatchQueue.global()

// 指定优先级
let highQueue = DispatchQueue.global(qos: .userInteractive)
let defaultQueue = DispatchQueue.global(qos: .default)
let backgroundQueue = DispatchQueue.global(qos: .background)

优先级(QoS - Quality of Service):

QoS 说明 使用场景
.userInteractive 用户交互 动画、UI 更新
.userInitiated 用户发起 用户操作触发的任务
.default 默认 一般任务
.utility 实用工具 下载、数据处理
.background 后台 后台任务
.unspecified 未指定 不推荐使用

三、执行方式详解

1. 同步执行(sync)

定义

同步执行会阻塞当前线程,等待任务完成后才继续执行。

swift 复制代码
queue.sync {
    // 任务代码
    print("执行任务")
}
// 这行代码会等待上面的任务完成才执行
特点
  • 阻塞当前线程:等待任务完成
  • 可以获取返回值:任务完成后返回结果
  • ⚠️ 可能死锁:在串行队列中嵌套使用要小心
代码示例
swift 复制代码
let queue = DispatchQueue(label: "example")

print("开始")
queue.sync {
    print("任务执行中")
    sleep(1)
    print("任务完成")
}
print("结束")

// 输出顺序(确定):
// 开始
// 任务执行中
// 任务完成
// 结束
应用场景
  • 需要等待任务完成再继续
  • 需要获取任务的返回值
  • 串行化访问共享资源

2. 异步执行(async)

定义

异步执行不阻塞当前线程,立即返回,任务稍后执行。

swift 复制代码
queue.async {
    // 任务代码
    print("执行任务")
}
// 这行代码立即执行,不等待上面的任务
特点
  • 不阻塞当前线程:立即返回
  • 提高响应性:UI 不会被阻塞
  • 推荐使用:大多数情况下使用 async
代码示例
swift 复制代码
let queue = DispatchQueue(label: "example")

print("开始")
queue.async {
    print("任务执行中")
    sleep(1)
    print("任务完成")
}
print("结束")

// 输出顺序(可能):
// 开始
// 结束  ← 立即执行
// 任务执行中
// 任务完成
应用场景
  • 大多数情况:推荐使用
  • 不阻塞 UI 线程
  • 后台处理任务
  • 提高响应性

四、组合使用

1. 四种组合方式

队列类型 执行方式 代码示例 说明 死锁风险
串行队列 sync serialQueue.sync { } 阻塞等待,按顺序执行 ⚠️ 高(嵌套)
串行队列 async serialQueue.async { } 不阻塞,按顺序执行 ✅ 低
并行队列 sync concurrentQueue.sync { } 阻塞等待,并发执行 ✅ 低
并行队列 async concurrentQueue.async { } 不阻塞,并发执行 ✅ 低

2. 实际应用示例

示例1:图片下载(并行队列 + async)
swift 复制代码
let imageQueue = DispatchQueue(label: "image", attributes: .concurrent)
let imageURLs = ["url1", "url2", "url3"]

for url in imageURLs {
    imageQueue.async {
        // 下载图片
        downloadImage(from: url)
    }
}
示例2:数据访问(串行队列 + sync)
swift 复制代码
class DataManager {
    private let serialQueue = DispatchQueue(label: "data")
    private var data: [String] = []
    
    func addItem(_ item: String) {
        serialQueue.async {
            self.data.append(item)
        }
    }
    
    func getItems() -> [String] {
        return serialQueue.sync {
            return self.data
        }
    }
}

五、GCD 高级特性

1. DispatchGroup(任务组)

定义

用于管理多个异步任务,等待所有任务完成。

基本使用
swift 复制代码
let group = DispatchGroup()
let queue = DispatchQueue.global()

// 添加任务到组
group.enter()
queue.async {
    print("任务1")
    group.leave()
}

group.enter()
queue.async {
    print("任务2")
    group.leave()
}

// 等待所有任务完成
group.notify(queue: .main) {
    print("所有任务完成")
}
实际应用:多图片下载
swift 复制代码
let group = DispatchGroup()
let imageURLs = ["url1", "url2", "url3"]

for url in imageURLs {
    group.enter()
    DispatchQueue.global().async {
        downloadImage(from: url) { image in
            // 处理图片
            group.leave()
        }
    }
}

group.notify(queue: .main) {
    print("所有图片下载完成")
    self.tableView.reloadData()
}
等待任务完成
swift 复制代码
let group = DispatchGroup()

group.enter()
queue.async {
    // 任务
    group.leave()
}

// 阻塞等待,直到所有任务完成
group.wait()
print("所有任务完成")

2. DispatchSemaphore(信号量)

定义

用于控制并发数量,限制同时执行的任务数。

基本使用
swift 复制代码
// 创建信号量,初始值为最大并发数
let semaphore = DispatchSemaphore(value: 3)

for i in 1...10 {
    DispatchQueue.global().async {
        // 等待信号量
        semaphore.wait()
        
        // 执行任务
        print("任务 \(i) 开始")
        sleep(1)
        print("任务 \(i) 结束")
        
        // 释放信号量
        semaphore.signal()
    }
}

// 最多同时执行 3 个任务
实际应用:控制网络请求并发数
swift 复制代码
class NetworkManager {
    private let semaphore = DispatchSemaphore(value: 5)  // 最多5个并发请求
    
    func fetchData(url: String, completion: @escaping (Data?) -> Void) {
        DispatchQueue.global().async {
            self.semaphore.wait()
            
            // 网络请求
            URLSession.shared.dataTask(with: URL(string: url)!) { data, _, _ in
                completion(data)
                self.semaphore.signal()
            }.resume()
        }
    }
}

3. DispatchBarrier(栅栏任务)- 控制任务依赖和执行顺序

定义

DispatchBarrier(栅栏任务) 是 GCD 中用于控制任务执行顺序的重要方法。在并行队列中:

  • 栅栏任务会等待前面的所有任务完成
  • 然后执行栅栏任务
  • 栅栏任务执行时,阻塞后面的任务
  • 栅栏任务完成后,后面的任务才能执行

核心作用: 在并行队列中创建一个"分界点",确保栅栏前后的任务按顺序执行。

基本使用
swift 复制代码
let concurrentQueue = DispatchQueue(
    label: "concurrent",
    attributes: .concurrent
)

// 栅栏前的任务:并发执行
concurrentQueue.async { 
    print("任务1开始")
    sleep(1)
    print("任务1结束")
}
concurrentQueue.async { 
    print("任务2开始")
    sleep(1)
    print("任务2结束")
}
concurrentQueue.async { 
    print("任务3开始")
    sleep(1)
    print("任务3结束")
}

// 栅栏任务:等待前面的任务完成,然后执行
concurrentQueue.async(flags: .barrier) {
    print("栅栏任务:前面的任务都完成了")
    print("栅栏任务执行中...")
    sleep(1)
    print("栅栏任务完成")
}

// 栅栏后的任务:等待栅栏任务完成后,并发执行
concurrentQueue.async { 
    print("任务4开始")
    sleep(1)
    print("任务4结束")
}
concurrentQueue.async { 
    print("任务5开始")
    sleep(1)
    print("任务5结束")
}

执行顺序:

markdown 复制代码
1. 任务1、2、3 并发执行(可能同时执行)
2. 等待任务1、2、3 全部完成
3. 执行栅栏任务(独占执行)
4. 栅栏任务完成后,任务4、5 并发执行
执行流程图
复制代码
并行队列:
┌─────────────────────────────────────────┐
│  栅栏前的任务(并发执行)                  │
│  ┌─────┐  ┌─────┐  ┌─────┐              │
│  │任务1│  │任务2│  │任务3│              │
│  └─────┘  └─────┘  └─────┘              │
│      ↓         ↓         ↓               │
│      └─────────┴─────────┘              │
│              ↓                           │
│  ┌─────────────────────┐                │
│  │   栅栏任务(独占)    │                │
│  │  (等待前面完成)     │                │
│  └─────────────────────┘                │
│              ↓                           │
│  栅栏后的任务(并发执行)                  │
│  ┌─────┐  ┌─────┐                        │
│  │任务4│  │任务5│                        │
│  └─────┘  └─────┘                        │
└─────────────────────────────────────────┘
关键特性
  1. 等待前面的任务完成

    • 栅栏任务会等待所有在它之前提交的任务完成
    • 无论这些任务是否已经开始执行
  2. 独占执行

    • 栅栏任务执行时,队列中的其他任务必须等待
    • 保证栅栏任务执行时不会有其他任务同时执行
  3. 阻塞后面的任务

    • 栅栏任务执行完成后,后面的任务才能开始执行
  4. 只在并行队列有效

    • 栅栏任务只在并行队列(.concurrent)中有效
    • 在串行队列中使用栅栏任务没有意义(串行队列本身就是顺序执行)
实际应用场景
应用1:读写锁(最常用)
swift 复制代码
class DataCache {
    private let concurrentQueue = DispatchQueue(
        label: "cache",
        attributes: .concurrent
    )
    private var cache: [String: Any] = [:]
    
    // 读操作:并发执行(多个读操作可以同时进行)
    func get(key: String) -> Any? {
        return concurrentQueue.sync {
            return cache[key]
        }
    }
    
    // 写操作:使用栅栏,保证独占访问(写操作时,读操作必须等待)
    func set(key: String, value: Any) {
        concurrentQueue.async(flags: .barrier) {
            self.cache[key] = value
        }
    }
    
    // 批量写操作:使用栅栏保证原子性
    func updateCache(_ updates: [String: Any]) {
        concurrentQueue.async(flags: .barrier) {
            for (key, value) in updates {
                self.cache[key] = value
            }
        }
    }
}

工作原理:

  • 多个读操作可以并发执行(性能高)
  • 写操作使用栅栏,确保写操作时没有读操作(数据安全)
  • 写操作完成后,读操作才能继续
应用2:数据同步点
swift 复制代码
class DataProcessor {
    private let queue = DispatchQueue(label: "processor", attributes: .concurrent)
    private var data: [Int] = []
    
    // 阶段1:并发处理数据
    func processData() {
        for i in 1...10 {
            queue.async {
                // 并发处理
                let result = self.processItem(i)
                // 注意:这里需要线程安全地添加数据
            }
        }
        
        // 栅栏任务:等待所有处理完成,然后同步数据
        queue.async(flags: .barrier) {
            print("所有数据处理完成,开始同步")
            self.syncData()
        }
        
        // 阶段2:同步完成后,继续处理
        queue.async {
            print("开始后续处理")
        }
    }
    
    private func processItem(_ item: Int) -> Int {
        return item * 2
    }
    
    private func syncData() {
        // 同步数据到数据库或服务器
    }
}
应用3:批量操作的分界点
swift 复制代码
class ImageDownloader {
    private let queue = DispatchQueue(label: "download", attributes: .concurrent)
    
    func downloadImages(_ urls: [String]) {
        // 阶段1:并发下载
        for url in urls {
            queue.async {
                self.downloadImage(url: url)
            }
        }
        
        // 栅栏任务:所有下载完成后,更新UI
        queue.async(flags: .barrier) {
            DispatchQueue.main.async {
                print("所有图片下载完成")
                self.updateUI()
            }
        }
    }
    
    private func downloadImage(url: String) {
        // 下载逻辑
    }
    
    private func updateUI() {
        // 更新UI
    }
}
应用4:确保初始化完成
swift 复制代码
class ResourceManager {
    private let queue = DispatchQueue(label: "resource", attributes: .concurrent)
    private var isInitialized = false
    
    func initialize() {
        // 初始化任务:并发执行
        queue.async {
            self.loadConfig()
        }
        queue.async {
            self.loadCache()
        }
        queue.async {
            self.loadData()
        }
        
        // 栅栏任务:等待所有初始化完成
        queue.async(flags: .barrier) {
            self.isInitialized = true
            print("初始化完成")
        }
    }
    
    func useResource() {
        queue.async(flags: .barrier) {
            guard self.isInitialized else {
                print("资源未初始化")
                return
            }
            // 使用资源
        }
    }
}
注意事项
  1. 只在并行队列有效

    swift 复制代码
    // ✅ 正确:在并行队列中使用
    let concurrentQueue = DispatchQueue(label: "queue", attributes: .concurrent)
    concurrentQueue.async(flags: .barrier) { }
    
    // ❌ 无效:在串行队列中使用栅栏没有意义
    let serialQueue = DispatchQueue(label: "queue")
    serialQueue.async(flags: .barrier) { }  // 串行队列本身就是顺序执行
  2. 栅栏任务会阻塞队列

    • 栅栏任务执行时,队列中的其他任务必须等待
    • 避免在栅栏任务中执行耗时操作
  3. 与 sync 的区别

    swift 复制代码
    // sync:阻塞当前线程,等待任务完成
    queue.sync { }  // 当前线程等待
    
    // barrier:在并行队列中创建分界点
    queue.async(flags: .barrier) { }  // 不阻塞当前线程,但阻塞队列中的其他任务
完整示例:线程安全的数据结构
swift 复制代码
class ThreadSafeArray<T> {
    private let queue = DispatchQueue(label: "array", attributes: .concurrent)
    private var array: [T] = []
    
    // 读操作:并发执行
    var count: Int {
        return queue.sync { array.count }
    }
    
    func get(at index: Int) -> T? {
        return queue.sync {
            guard index < array.count else { return nil }
            return array[index]
        }
    }
    
    // 写操作:使用栅栏,保证独占访问
    func append(_ element: T) {
        queue.async(flags: .barrier) {
            self.array.append(element)
        }
    }
    
    func remove(at index: Int) {
        queue.async(flags: .barrier) {
            guard index < self.array.count else { return }
            self.array.remove(at: index)
        }
    }
    
    // 批量操作:使用栅栏保证原子性
    func replaceAll(with newArray: [T]) {
        queue.async(flags: .barrier) {
            self.array = newArray
        }
    }
}
总结

DispatchBarrier 的核心作用:

  • ✅ 在并行队列中创建"分界点"
  • ✅ 确保栅栏前的任务全部完成
  • ✅ 栅栏任务独占执行
  • ✅ 栅栏后的任务等待栅栏完成

适用场景:

  • 读写锁实现
  • 数据同步点
  • 批量操作的分界
  • 初始化完成确认
  • 线程安全的数据结构

4. DispatchSource(事件源)

定义

用于监听系统事件,如定时器、文件变化、信号等。

定时器示例
swift 复制代码
// 创建定时器
let timer = DispatchSource.makeTimerSource(queue: .global())
timer.schedule(deadline: .now(), repeating: 1.0)
timer.setEventHandler {
    print("定时器触发")
}
timer.resume()

// 取消定时器
// timer.cancel()
文件监控示例
swift 复制代码
let fileURL = URL(fileURLWithPath: "/path/to/file")
let fileDescriptor = open(fileURL.path, O_EVTONLY)

let source = DispatchSource.makeFileSystemObjectSource(
    fileDescriptor: fileDescriptor,
    eventMask: .write,
    queue: .global()
)

source.setEventHandler {
    print("文件被修改")
}

source.resume()

5. DispatchWorkItem(任务封装)

定义

将任务封装成对象,可以取消、等待、通知。

基本使用
swift 复制代码
// 创建任务
let workItem = DispatchWorkItem {
    print("执行任务")
}

// 执行任务
DispatchQueue.global().async(execute: workItem)

// 等待任务完成
workItem.wait()

// 取消任务(如果还没开始执行)
workItem.cancel()
任务通知
swift 复制代码
let workItem = DispatchWorkItem {
    print("任务执行")
}

workItem.notify(queue: .main) {
    print("任务完成")
}

DispatchQueue.global().async(execute: workItem)

六、实际应用场景

场景1:图片下载和显示

swift 复制代码
func loadImage(url: String, completion: @escaping (UIImage?) -> Void) {
    DispatchQueue.global().async {
        // 在后台线程下载
        guard let imageURL = URL(string: url),
              let data = try? Data(contentsOf: imageURL),
              let image = UIImage(data: data) else {
            completion(nil)
            return
        }
        
        // 回到主线程更新 UI
        DispatchQueue.main.async {
            completion(image)
        }
    }
}

// 使用
loadImage(url: "https://example.com/image.jpg") { image in
    self.imageView.image = image
}

场景2:批量数据处理

swift 复制代码
func processLargeData(data: [Int], completion: @escaping ([Int]) -> Void) {
    let group = DispatchGroup()
    let queue = DispatchQueue.global()
    var results: [Int] = []
    let lock = NSLock()
    
    // 分批处理
    let batchSize = 1000
    for i in stride(from: 0, to: data.count, by: batchSize) {
        let end = min(i + batchSize, data.count)
        let batch = Array(data[i..<end])
        
        group.enter()
        queue.async {
            // 处理批次
            let processed = batch.map { $0 * 2 }
            
            lock.lock()
            results.append(contentsOf: processed)
            lock.unlock()
            
            group.leave()
        }
    }
    
    group.notify(queue: .main) {
        completion(results)
    }
}

场景3:网络请求管理

swift 复制代码
class APIManager {
    private let semaphore = DispatchSemaphore(value: 3)  // 最多3个并发
    
    func request(url: String, completion: @escaping (Data?) -> Void) {
        DispatchQueue.global().async {
            self.semaphore.wait()
            
            URLSession.shared.dataTask(with: URL(string: url)!) { data, _, _ in
                completion(data)
                self.semaphore.signal()
            }.resume()
        }
    }
}

七、常见问题和陷阱

问题1:死锁

主队列同步调用
swift 复制代码
// ❌ 死锁
DispatchQueue.main.sync {
    print("这行代码永远不会执行")
}
串行队列嵌套同步调用
swift 复制代码
// ❌ 死锁
let serialQueue = DispatchQueue(label: "serial")
serialQueue.sync {
    serialQueue.sync {
        print("这行代码永远不会执行")
    }
}
解决方案
swift 复制代码
// ✅ 使用 async
DispatchQueue.main.async {
    print("这行代码会执行")
}

// ✅ 使用不同的队列
let serialQueue = DispatchQueue(label: "serial")
serialQueue.async {
    DispatchQueue.main.sync {
        print("这行代码会执行")
    }
}

问题2:循环引用

swift 复制代码
// ❌ 循环引用
class ViewController: UIViewController {
    func loadData() {
        DispatchQueue.global().async {
            self.processData()  // 捕获了 self
        }
    }
}

// ✅ 使用 weak self
class ViewController: UIViewController {
    func loadData() {
        DispatchQueue.global().async { [weak self] in
            self?.processData()
        }
    }
}

问题3:线程安全

swift 复制代码
// ❌ 线程不安全
class Counter {
    var count = 0
    
    func increment() {
        count += 1  // 多线程访问可能出错
    }
}

// ✅ 使用串行队列保护
class Counter {
    private let queue = DispatchQueue(label: "counter")
    private var _count = 0
    
    var count: Int {
        return queue.sync { _count }
    }
    
    func increment() {
        queue.async {
            self._count += 1
        }
    }
}

八、性能优化建议

1. 合理选择队列类型

  • 串行队列:需要顺序执行、线程安全
  • 并行队列:独立任务、性能优化

2. 控制并发数量

swift 复制代码
// 使用信号量控制并发数
let semaphore = DispatchSemaphore(value: 5)

3. 避免过度使用 sync

swift 复制代码
// ❌ 过度使用 sync 会阻塞线程
for i in 1...100 {
    queue.sync {
        process(i)
    }
}

// ✅ 使用 async 提高性能
for i in 1...100 {
    queue.async {
        process(i)
    }
}

4. 使用合适的 QoS

swift 复制代码
// 根据任务重要性设置优先级
DispatchQueue.global(qos: .userInteractive).async {
    // 用户交互相关
}

DispatchQueue.global(qos: .background).async {
    // 后台任务
}

九、面试常见问题

Q1: GCD 和 OperationQueue 的区别?

回答要点:

  • GCD 更底层,基于 C 语言,性能更好
  • OperationQueue 基于 GCD,提供更多功能(依赖、取消、优先级)
  • GCD 适合简单任务,OperationQueue 适合复杂任务管理

Q2: 如何实现线程安全?

回答要点:

  1. 使用串行队列保护共享资源
  2. 使用锁机制(NSLock、NSRecursiveLock)
  3. 使用 DispatchBarrier 实现读写锁
  4. 使用原子操作(atomic)

Q3: DispatchGroup 和 DispatchSemaphore 的区别?

回答要点:

  • DispatchGroup:管理多个任务,等待所有任务完成
  • DispatchSemaphore:控制并发数量,限制同时执行的任务数

Q4: 如何避免死锁?

回答要点:

  1. 避免在主线程使用 DispatchQueue.main.sync
  2. 避免在串行队列中嵌套同步调用
  3. 使用 async 代替 sync
  4. 使用不同的队列避免循环等待

十、总结

GCD 核心要点

  1. 队列类型:串行队列 vs 并行队列
  2. 执行方式:同步(sync)vs 异步(async)
  3. 系统队列:主队列(main)vs 全局队列(global)
  4. 高级特性:Group、Semaphore、Barrier、Source

最佳实践

  1. ✅ 大多数情况使用 async
  2. ✅ UI 更新必须在主队列
  3. ✅ 耗时操作放在后台队列
  4. ✅ 使用串行队列保护共享资源
  5. ✅ 使用信号量控制并发数
  6. ✅ 注意循环引用和线程安全

面试回答模板

问题:介绍一下 GCD?

"GCD 是 Grand Central Dispatch 的缩写,是 iOS 开发中最常用的多线程方案。

核心概念:

  • 队列分为串行队列和并行队列
  • 执行方式分为同步和异步
  • 主队列用于 UI 更新,全局队列用于后台任务

常用功能:

  • dispatch_async 异步执行任务
  • DispatchGroup 管理多个异步任务
  • DispatchSemaphore 控制并发数量
  • DispatchBarrier 实现读写锁
  • DispatchSource 监听系统事件

注意事项:

  • 主队列同步调用会死锁
  • 耗时操作放在后台队列,UI 更新回到主队列
  • 注意线程安全和内存管理(循环引用)

我在项目中常用 GCD 处理网络请求、图片加载、数据处理等异步任务,确保主线程不被阻塞,保证 UI 流畅性。"


最后更新:2024

相关推荐
winfredzhang2 小时前
零基础打造轻量级桌面备忘录:Electron 核心技术实战
前端·javascript·electron·备忘录
代码猎人2 小时前
如何实现深拷贝
前端
大鸡爪2 小时前
《Vue3 组件库搭建指北:pnpm + monorepo + 代码提交规范+ BEM 环境配置》
前端·vue.js
UIUV2 小时前
TypeScript深度学习笔记:从动态语言到强类型工程化实践
前端·typescript
代码猎人2 小时前
箭头函数与普通函数有哪些区别
前端
Lazy_zheng2 小时前
一文搞懂 JavaScript 数据类型转换(显式 & 隐式全解析)
前端·javascript·面试
小宇的天下2 小时前
Virtuoso 中的tech file 详细说明
java·服务器·前端
Zoey的笔记本2 小时前
「软件开发缺陷管理工具」的闭环追踪设计与自动化集成架构
java·服务器·前端
Sapphire~2 小时前
【前端基础】04-XSS(跨站脚本攻击,Cross-Site Scripting)
前端·xss