1、队列 DispatchQueue
特点:先进先出
分类:串行队列、迸发队列
1.1 主队列
DispatchQueue.main
是一种串行队列(Serial),与主线程关联的调度队列,与UI相关的操作必须放在 主队列中执行。
1.2 全局队列
DispatchQueue.global()
是一种并行队列(Concurrent),用于处理并发任务。运行在后台线程,是系统内共享的全局队列。
1.3 自定义队列
DispatchQueue 是 Swift 中用于管理并发任务的类,通过它可以将任务放到不同的队列中执行。
- 创建串行队列
把任务添加到串行队列中执行,无论是同步sync,还是异步方式async,都将按顺序执行。
通常用来确保任务按能顺序执行。线程同步。
let serialQueue = DispatchQueue(label: "com.example.serialQueue")
serialQueue.async {
print("Task 11111")
}
serialQueue.async {
print("Task 22222")
}
// 这两个任务会 按顺序执行,Task 1 -> Task 2
label: 队列的标识符,确保唯一性。标签仅用于调试或日志目的。
- 创建迸发队列
加个参数 attributes
let concurrentQueue = DispatchQueue(label: "com.example.concurrentQueue",
attributes: .concurrent)
concurrentQueue.async {
print("Task A")
}
concurrentQueue.async {
print("Task B")
}
把任务添加到迸发队列中执行,若是用异步方式async,任务会并发执行,执行顺序不固定。若用同步方式sync则仍然是按顺序执行。
DispatchGroup
是 GCD 的任务组,可以很方便的管理多项任务。用来 管理一组异步任务的执行进度,非常适合在多个异步任务完成后,再统一处理结果。是常用的一个同步机制。
优点:不会造成线程阻塞!
“当 A、B、C 三个任务都完成后,再执行 D。”
常用方法:
-
enter():表示有一个任务加入组
-
leave():表示有一个任务离开组(该任务已完成)
-
notify(): 当所有任务都 leave 之后,会自动触发
-
wait():一直等待,直到调度组里所有任务都执行完毕或等待超时,阻塞当前线程。
场景1:等待多任务完成后统一回调
let group = DispatchGroup()
let queue = DispatchQueue.global()
group.enter()
queue.async {
print("下载图片1开始")
Thread.sleep(forTimeInterval: 2)
print("下载图片1完成")
group.leave()
}
group.enter()
queue.async {
print("下载图片2开始")
Thread.sleep(forTimeInterval: 3)
print("下载图片2完成")
group.leave()
}
// 所有任务完成后通知
group.notify(queue: .main) {
print("所有下载完成,更新UI")
}
也可以简化写法:(不需要手动调用 enter()、leave())
let group = DispatchGroup()
let queue = DispatchQueue.global()
queue.async(group: group) {
Thread.sleep(forTimeInterval: 1)
print("任务 A 完成")
}
queue.async(group: group) {
Thread.sleep(forTimeInterval: 2)
print("任务 B 完成")
}
group.notify(queue: .main) {
print("A 和 B 都完成了")
}
- 同步等待:group.wait()
如果你希望“阻塞”当前线程,直到所有任务执行完。(不建议在主线程中调用,会阻塞 UI)
let group = DispatchGroup()
let queue = DispatchQueue.global()
queue.async(group: group) {
Thread.sleep(forTimeInterval: 1)
print("任务1完成")
}
queue.async(group: group) {
Thread.sleep(forTimeInterval: 2)
print("任务2完成")
}
group.wait() // 阻塞当前线程,直到所有任务完成
print("所有任务完成后 执行")
可设置超时
let result = group.wait(timeout: .now() + 5)
if result == .timedOut {
print("任务超时")
}
DispatchQueue.concurrentPerform
GCD提供的一种并发执行多个任务的方法。它用于高效地在多核处理器上并行执行一系列操作,特别是在处理重复任务时,利用多线程提高性能。
DispatchQueue.concurrentPerform(iterations: n) { index in
// 并发执行的代码块
}
其中
• iterations: n:执行的次数。代码块 会循环执行n次。
• { index in }:这是一个闭包,闭包的参数 index 是当前执行的索引,从 0 到 n-1。在闭包内的代码将会并发执行多次,每次的索引都不相同。
- 特点
1、并发执行:DispatchQueue.concurrentPerform 在内部会创建多个线程,并在这些线程中并发执行闭包任务。这是一个同步调用,意味着当前线程会阻塞,直到所有任务执行完毕。
因此,如果你希望任务在后台线程并发执行,可以在全局队列中使用它:
DispatchQueue.global().async {
DispatchQueue.concurrentPerform(iterations: 10) { index in
// 并发任务
}
}
2、提高性能:DispatchQueue.concurrentPerform 可以利用多核 CPU,并发执行 计算密集型 或 IO 密集型任务,从而提高性能。适用于需要并行执行的大量重复任务,比如数组遍历、矩阵运算、大规模数据处理等计算密集型场景。
应用场景:在我们使用for循环的时候,其中的语句总是在前一个for完成之后才执行下一个。当执行大批量任务时,如果其中的任务相互独立,可以使用
DispatchQueue.concurrentPerform来使用多线程平行的同步执行这些任务来节省时间。
死锁
线程安全
barrier
DispatchSemaphore
是 GCD 提供的一种 信号量(计数器),用来控制并发线程的数量,或者在多线程间实现同步。
它内部维护了一个整型计数值:
- 大于 0 表示有可用资源,调用 wait() 会立即成功并将计数 减 1。
- 等于 0 表示没有可用资源,此时调用 wait() 会阻塞当前线程,直到有资源释放。
- 调用 signal() 会让计数加 1,并唤醒等待的线程。(如果有多个线程在等待,则唤醒其中一个)
使用场景
1、控制并发数量(限流)
let semaphore = DispatchSemaphore(value: 3) // 最多允许3个并发任务
for i in 1...10 {
DispatchQueue.global().async {
semaphore.wait() // 占用一个名额
print("开始任务 \(i)")
// do something....
print("完成任务 \(i)")
semaphore.signal() // 释放一个名额
}
}
2、线程同步(等待异步任务完成)
func fetchDataSync(completion: @escaping (String) -> Void) {
let semaphore = DispatchSemaphore(value: 0)
var result: String?
DispatchQueue.global().async {
// 模拟网络请求
Thread.sleep(forTimeInterval: 2)
result = "数据返回"
semaphore.signal() // 信号量+1,唤醒等待的线程
}
semaphore.wait() // 信号量=0,阻塞当前线程,等待异步完成, 才能继续执行
completion(result ?? "")
}
// 调用
fetchDataSync { data in
print("拿到数据: \(data)")
}
3、资源互斥访问
多个线程需要安全访问同一个变量。保证临界区只有一个线程在执行
let semaphore = DispatchSemaphore(value: 1) // 相当于互斥锁
var count = 0
DispatchQueue.concurrentPerform(iterations: 10) { _ in
semaphore.wait()
count += 1
semaphore.signal()
}
print("最终 count = \(count)") // 10
串行队列+计算属性
var b:Int{
get{
queue.sync {
print("同步读取 thread = \(Thread.current)")
return a
}
}
set{
queue.sync {
print("同步写入 thread = \(Thread.current)")
a = newValue
}
}
}
注意:
- DispatchSemaphore 适合做简单的同步或限流,更复杂的情况可用
OperationQueue或DispatchGroup - 避免死锁:如果
wait()后忘记signal(),线程可能永远卡住。 - 不要在主线程阻塞:在主线程调用
wait()可能导致界面卡死。