聊聊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

相关推荐
崔庆才丨静觅3 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60613 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了4 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅4 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅4 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅4 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment5 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅5 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊5 小时前
jwt介绍
前端
爱敲代码的小鱼5 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax