DispatchQueue
是 GCD(Grand Central Dispatch)中最常用的类之一,它提供了一种基于队列(queue)的多任务并发管理方式,允许在并发操作中调度任务。它的主要功能是在主线程或后台线程上执行同步任务或异步任务,使用起来高效又直观。
以下是对 DispatchQueue
的接口结构、功能说明以及使用方法的详细介绍。
1. DispatchQueue 功能概览 #
DispatchQueue
基于先进先出 (FIFO) 队列运行。它负责管理任务的调度并将其分发到相应的线程上。
主要功能: #
- 控制任务在主线程或后台线程上运行。
- 支持同步(
sync
)和异步(async
)任务。 - 提供串行队列和并发队列,以灵活实现任务调度。
- 提供 QoS(Quality of Service,服务质量)设置,用于控制任务优先级。
- 借助定时器和
DispatchWorkItem
提供延迟执行、循环执行以及任务取消功能。
2. DispatchQueue 的接口结构 #
DispatchQueue
主要通过以下方法和接口调度任务:
创建队列 #
方法 | 说明 |
---|---|
DispatchQueue.main | 获取主队列,用于在主线程运行任务。 |
DispatchQueue.global(qos: DispatchQoS) | 获取全局并发队列,可以指定 QoS 切换任务的优先级。 |
DispatchQueue(label:) | 创建自定义队列(串行或并发)。默认是串行队列,可通过配置使其提供并发功能。 |
任务执行方式 #
方法 | 说明 |
---|---|
async {} | 异步任务,立即返回,不阻塞当前线程。 |
sync {} | 同步任务,阻塞当前线程直到任务完成。 |
asyncAfter(deadline:execute:) | 延迟调度任务。 |
其它实用功能 #
方法 | 说明 |
---|---|
DispatchWorkItem | 包装任务,可以控制任务的状态,例如取消任务。 |
DispatchSourceTimer | 创建定时器任务。 |
3. DispatchQueue 的使用说明 #
以下是 DispatchQueue
的一些常见用法和代码示例:
(1) 使用主队列 #
主队列是一种特殊的串行队列,所有的任务会在主线程执行,适用于更新 UI 操作。
DispatchQueue.main.async {
// 在主线程上运行的任务,通常是刷新 UI
someView.text = "Hello, DispatchQueue!"
}
(2) 使用全局并发队列 #
使用全局并发队列适合执行耗时任务,例如下载文件或后台数据处理。可以通过 qos
参数指定任务的优先级。
- 常见的 QoS:
.userInteractive
:用户交互任务,需立即执行(如 UI 刷新)。.userInitiated
:用户发起的任务(优先级高,如文件打开)。.utility
:后台长时间运行的任务(如文件下载、数据分析)。.background
:低优先级任务(如预加载)。
// 从全局并发队列调度任务(优先级设置为 .userInitiated)
DispatchQueue.global(qos: .userInitiated).async {
print("正在下载数据")
// 模拟耗时操作
Thread.sleep(forTimeInterval: 2)
DispatchQueue.main.async {
print("UI 更新完成")
}
}
(3) 创建自定义队列 #
串行队列(Serial Queue) #
串行队列中的任务按顺序一个接一个执行。
let serialQueue = DispatchQueue(label: "com.example.serialQueue")
serialQueue.async {
print("任务 1 开始")
Thread.sleep(forTimeInterval: 1) // 模拟耗时任务
print("任务 1 完成")
}
serialQueue.async {
print("任务 2 开始")
Thread.sleep(forTimeInterval: 1)
print("任务 2 完成")
}
并发队列(Concurrent Queue) #
并发队列中的任务可以同时执行,提高执行效率:
let concurrentQueue = DispatchQueue(label: "com.example.concurrentQueue", attributes: .concurrent)
concurrentQueue.async {
print("任务 A 开始")
Thread.sleep(forTimeInterval: 1)
print("任务 A 完成")
}
concurrentQueue.async {
print("任务 B 开始")
Thread.sleep(forTimeInterval: 2)
print("任务 B 完成")
}
concurrentQueue.async {
print("任务 C 开始")
Thread.sleep(forTimeInterval: 0.5)
print("任务 C 完成")
}
(4) 延迟执行任务 #
通过 asyncAfter
方法,可以延迟指定时间后执行任务。例如:
let delayQueue = DispatchQueue(label: "com.example.delay")
let delayTime: DispatchTime = .now() + 2 // 延迟 2 秒
delayQueue.asyncAfter(deadline: delayTime) {
print("延迟 2 秒后执行任务")
}
(5) DispatchWorkItem:控制和取消任务 #
通过 DispatchWorkItem
,你可以封装任务并在需要时取消它。
let workItem = DispatchWorkItem {
print("执行工作任务")
}
// 提交任务
DispatchQueue.global().async(execute: workItem)
// 如果需要取消任务
workItem.cancel()
在任务执行前检查是否已被取消:
let workItem = DispatchWorkItem {
if workItem.isCancelled {
print("任务已取消")
return
}
print("执行工作任务")
}
DispatchQueue.global().async(execute: workItem)
(6) 定时器任务 #
创建一个定时任务,使用 DispatchSourceTimer
。
let timer = DispatchSource.makeTimerSource()
// 配置定时器:延迟 1 秒开始执行,并每隔 2 秒执行一次
timer.schedule(deadline: .now() + 1, repeating: 2)
timer.setEventHandler {
print("定时器触发任务")
}
// 启动定时器
timer.activate()
// 停止定时器任务
DispatchQueue.global().asyncAfter(deadline: .now() + 10) {
timer.cancel()
print("定时器任务结束")
}
(7) 同步任务 (sync
) 的使用
#
同步任务会阻塞当前线程,直到任务完成。避免在主线程中使用它以防止死锁。
let serialQueue = DispatchQueue(label: "com.example.serialQueue")
serialQueue.sync {
print("同步任务 1")
}
serialQueue.sync {
print("同步任务 2")
}
4. DispatchQueue 的注意事项 #
避免主线程阻塞
- 不要在主线程上运行耗时任务(如文件 I/O、网络请求),否则会导致 UI 卡顿。
死锁问题
- 如果主线程中调用同步任务(
sync
),而该任务又等待主线程完成,容易引发死锁:DispatchQueue.main.sync { print("死锁会发生!") }
- 如果主线程中调用同步任务(
QoS 的使用
- 为不同任务设置不同的 QoS 以优化系统资源使用。
任务取消
- 使用
DispatchWorkItem
可以追踪和取消任务,但实际已启动的任务不能被实际中断。
- 使用
5. 总结与应用场景 #
使用场景 | 解决方法 |
---|---|
主线程更新 UI | DispatchQueue.main.async |
后台处理任务 | DispatchQueue.global().async 或自定义队列 |
任务控制及取消 | 使用 DispatchWorkItem |
延迟任务 | 使用 .asyncAfter |
循环/定时任务 | 使用 DispatchSourceTimer |
DispatchQueue
是高效的任务管理工具,通过它你可以灵活调度任务、优化并发性能。