使用 DispatchGroup、DispatchQueue 和 DispatchSemaphore 按顺序执行带有 for 循环的 Swift 4 异步调用
Posted
技术标签:
【中文标题】使用 DispatchGroup、DispatchQueue 和 DispatchSemaphore 按顺序执行带有 for 循环的 Swift 4 异步调用【英文标题】:Swift 4 async call with for loop execute in order using DispatchGroup, DispatchQueue and DispatchSemaphore 【发布时间】:2017-10-21 12:43:22 【问题描述】:我想在 order 中快速运行 for 循环,DispatchGroup 会将它们一起触发,所以我想使用 DispatchQueue 和 DispatchSemaphore 来实现我的目标。我的程序无法运行,如何强制它们等待并一个一个运行?
let dispatchGroup = DispatchGroup()
let dispatchQueue = DispatchQueue(label: "taskQueue")
let dispatchSemaphore = DispatchSemaphore(value: 1)
for c in self.categories
dispatchSemaphore.wait()
dispatchQueue.async(group: dispatchGroup)
if let id = c.categoryId
dispatchGroup.enter()
self.downloadProductsByCategory(categoryId: id) success, data in
if success, let products = data
self.products.append(products)
dispatchSemaphore.signal()
dispatchGroup.leave()
dispatchGroup.notify(queue: dispatchQueue)
self.refreshOrderTable _ in
self.productCollectionView.reloadData()
NVActivityIndicatorPresenter.sharedInstance.stopAnimating()
感谢 Palle,这是我的最终代码:
let dispatchGroup = DispatchGroup()
let dispatchQueue = DispatchQueue(label: "taskQueue")
let dispatchSemaphore = DispatchSemaphore(value: 0)
dispatchQueue.async
for c in self.categories
if let id = c.categoryId
dispatchGroup.enter()
self.downloadProductsByCategory(categoryId: id) success, data in
if success, let products = data
self.products.append(products)
dispatchSemaphore.signal()
dispatchGroup.leave()
dispatchSemaphore.wait()
dispatchGroup.notify(queue: dispatchQueue)
DispatchQueue.main.async
self.refreshOrderTable _ in
self.productCollectionView.reloadData()
【问题讨论】:
不相关但不要让weak self
- strong self
跳舞。这很荒谬,因为 GCD 确实不会导致保留周期。
如果要按顺序执行,为什么还要使用并发呢?只需在后台队列中创建一个循环并直接在循环中执行您的downloadProductsByCategory
函数。
@Palle downloadProductsByCategory
本身就是使用 alamofire 的异步方法
然后将dispatchSemaphore.wait()
放在完成处理程序中对downloadProductsByCategory
和dispatchSemaphore.signal()
的调用之后。
@Palle 出于某种原因,当dispatchSemaphore.wait()
调用第二次时,整个程序将永远被阻塞在那里。和downloadProductsByCategory
永远不会收到回调
【参考方案1】:
您可以将整个循环放在一个块中,而不是将下载功能放在一个块中:
dispatchQueue.async
for c in self.categories
if let id = c.categoryId
self.downloadProductsByCategory(categoryId: id) success, data in
if success, let products = data
self.products.append(products)
dispatchSemaphore.signal()
dispatchSemaphore.wait()
您可以通过使用 compactMap 来解包您的产品 ID 来简化您的代码:
for id in self.categories.compactMap($0.categoryId) ...
【讨论】:
谢谢,它有效。但还有一件事,当我获得所有数据时如何获得通知?我可以在当前代码中使用DispatchGroup
吗?
完成循环后,只需在块中添加DispatchQueue.main.async ...
。具有单个 enter
和 leave
的调度组当然也可以工作。
当代码本身是同步的时,确实不需要调度组。这没有意义。只需在你的 for 循环之后调用你的完成块。
这个答案中的dispatchSemaphore
不是在任何地方创建的。
信号量初始化为0:let dispatchSemaphore = DispatchSemaphore(value: 0)
之前。您可以在问题中看到完整的最终解决方案。以上是关于使用 DispatchGroup、DispatchQueue 和 DispatchSemaphore 按顺序执行带有 for 循环的 Swift 4 异步调用的主要内容,如果未能解决你的问题,请参考以下文章
iOS开发:深入理解GCD 第二篇(dispatch_groupdispatch_barrier基于线程安全的多读单写)
通过 DispatchGroup 与 DispatchQueue 访问主队列
等待循环完成,DispatchGroup 使用 Swift 从 firebase 获取数据
使用 DispatchGroup、DispatchQueue 和 DispatchSemaphore 按顺序执行带有 for 循环的 Swift 4 异步调用