这时候我们就可以用DispatchGroup
来处理
DispatchGroup
基础使用,追踪不同队列中的任务。
不同队列可以用来分别处理不同优先级的任务
let group = DispatchGroup()
someQueue.async(group: group) { ... 一些任务 ... }
someQueue.async(group: group) { ... 一些任务 .... }
someOtherQueue.async(group: group) { ... 其它任务 ... }
// 任务全部完成之后会在指定的队列执行
group.notify(queue: DispatchQueue.main) { [weak self] in
guard let self = self else { return }
self.textLabel.text = "全部任务完成"
}
wait
let group = DispatchGroup()
let queue = DispatchQueue.global(qos: .userInitiated)
queue.async(group: group) {
print("开始任务1")
Thread.sleep(until: Date().addingTimeInterval(4)) // a
print("结束任务1")
}
queue.async(group: group) {
print("开始任务2")
Thread.sleep(until: Date().addingTimeInterval(2)) // b
print("结束任务2")
}
if group.wait(timeout: .now() + 5) == .timedOut { // c
print("啊!我等不下去了!😭")
} else {
print("所有任务都已经完成😌")
}
上述代码会输出
开始任务1
开始任务2
结束任务2
结束任务1
所有任务都已经完成😌
如果你把注释a
的4秒改成6秒,则会输出
开始任务1
开始任务2
结束任务2
啊!我等不下去了!😭
结束任务1
注意输出的结束任务1
, wait
只是设置了一个阈值来监测任务会不会在规定的时间内完成,并不会停止还未完成的任务
注意,
wait
会阻塞当前线程,所以千万不要在主线程调用
enter()
和leave()
Dispatch队列会在闭包内的代码执行完的时候自动通知DispatchGroup.
但是如果你在Dispatch闭包里调用了一个异步请求,问题就来了,Dispatch内的代码很有可能在异步请求完成之前就跑完了,然后就告诉Group"这里的任务已经完成了", 但实际并不符合我们的期望。
这时候,我们就可以用group.enter()
和group.leave()
来控制。
这对命令其实就相当于是在计数,当你enter()
的时候,运行中的任务计数+1; 当你leave()
的时候,运行中的任务-1。
queue.dispatch(group: group) {
// count = 1
group.enter()
// count = 2
someAsyncMethod {
// 知行业务逻辑
group.leave() // count -= 1
}
// count -= 1
}
Semaphore
当需要对每个资源精确地限制访问量的时候就可以用Semaphore
(信号量)了
假设现在有个批量下载任务,你最多允许3个下载任务同时进行
先创建一个只最多允许三个访问的Semaphore
let semaphore = DispatchSemaphore(value: 3)
模拟10个下载任务
for i in 1...10 {
queue.async(group: group) {
semaphore.wait() // Semaphore计数+1
print("正在下载第\(i)张图")
// 模拟网络等待
Thread.sleep(forTimeInterval: 2)
print("第\(i)张图已下载")
semaphore.signal() // Semaphore计数-1
}
}
上述代码你会一下子看到
“正在下第1张图”
“正在下第2张图”
“正在下第3张图”
然后直到2秒后,下载任务才会继续执行
小结
从代码实现的角度,对于某些业务逻辑,比如下载10张图片完成后刷新界面,上述两种方法都可以达到,但是从Dispatch的功能设计出发,优雅的使用方法是
-
DispatchGroup
用于跟踪任务完成状态,然后在批量任务完成/未完成的时候来处理业务逻辑 -
DispatchSemorephore
用于控制访问数量
系列文章链接