DispatchGroup
当您希望跟踪一组任务的完成情况时,可以使用DispatchGroup。
let group = DispatchGroup()
someQueue.async(group: group) { … your work … } someQueue.async(group: group) { … more work …. } someOtherQueue.async(group: group) { … other work … }
group.notify(queue: DispatchQueue.main) { [weak self] in
self?.textLabel.text = “All jobs have completed”
}
正如上面的示例代码所示,DispatchGroup没有要求只能有一个queue。
可以使用单个DispatchGroup,但根据需要运行的任务的优先级,将任务提交到多个队列。
DispatchGroups提供一个notify(queue:)方法,您可以使用该方法在提交的每个任务完成后立即收到通知。
通知本身是异步的,所以在调用notify之后,只要先前提交的任务尚未完成,就可以向组提交更多任务。
Synchronous waiting
如果由于某种原因不能异步响应组的完成通知,则可以在DispatchGroup上使用wait方法。 这是一个同步方法,它将阻塞当前队列,直到所有作业都完成。 它使用一个可选参数,该参数指定等待任务完成所需的时间。如果没有指定,则有无限的等待时间,所以千万不要在主线程上调用:
let group = DispatchGroup()
someQueue.async(group: group) { … } someQueue.async(group: group) { … } someOtherQueue.async(group: group) { ... }
if group.wait(timeout: .now() + 60) == .timedOut {
print("The jobs didn’t finish in 60 seconds") }
Wrapping asynchronous methods
因为如果在闭包内部调用异步方法,那么闭包将在内部异步方法完成之前完成。
所以必须以某种方式告诉任务,直到那些内部调用也完成了,它才会完成。
在这种情况下,您可以调用DispatchGroup上提供的enter和leave方法。
将它们看作是运行任务的简单计数。每次你进去,人数就增加1。当你离开的时候,人数减少了1:
queue.dispatch(group: group) {
// count is 1
group.enter()
// count is 2
someAsyncMethod {
defer { group.leave() }
// Perform your work here,
// count goes back to 1 once complete
} }
通过调用group.enter(),可以让DispatchGroup知道还有另一个代码块正在运行,这应该计入组的总体完成状态。
必须将其与对应的group.leave()调用配对,否则永远不会有完成的信号。
因为即使在出现错误的情况下也必须调用leave,所以通常需要使用一条defer语句,如上所示,无论如何退出闭包,group.leave()代码都会执行。
Semaphores
有时候确实需要控制有多少线程可以访问共享资源。
已经看到了限制对单个线程的访问的读/写模式,但是有时您可以允许一次使用更多的资源,同时仍然保持对总线程数的控制。
例如正在从网络下载数据,可能希望限制一次下载的次数。
您将使用一个DispatchQueue来卸载工作,并使用DispatchGroup以便您知道所有下载何时完成。但是只希望同时进行四次下载,因为知道要处理的数据非常大,而且资源非常多。
通过使用DispatchSemaphore,可以准确地处理该用例。在使用资源之前,只需调用wait方法,这是一个同步函数,线程将暂停执行,直到资源可用为止。如果还没有所有权,您将立即获得访问权。如果别人有,你就等着,直到他们发出结束的信号。
在创建Semaphores时,指定允许多少对资源的并发访问。如果希望同时启用4个网络下载,则传入4个。如果您试图为独占访问锁定资源,那么只需指定1。
let semaphore = DispatchSemaphore(value: 4)
for i in 1…10 { queue.async(group: group) {
semaphore.wait()
defer { semaphore.signal() }
print(“Downloading image \(i)”)
// Simulate a network wait
Thread.sleep(forTimeInterval: 3) print(“Downloaded image \(i)”)
} }
过程:立即看到发生了4次下载,然后,3秒钟后,又发生了4次下载。最后3秒钟后,最后2项完成。