使用 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() 放在完成处理程序中对downloadProductsByCategorydispatchSemaphore.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 ... 。具有单个 enterleave 的调度组当然也可以工作。 当代码本身是同步的时,确实不需要调度组。这没有意义。只需在你的 for 循环之后调用你的完成块。 这个答案中的dispatchSemaphore 不是在任何地方创建的。 信号量初始化为0:let dispatchSemaphore = DispatchSemaphore(value: 0)之前。您可以在问题中看到完整的最终解决方案。

以上是关于使用 DispatchGroup、DispatchQueue 和 DispatchSemaphore 按顺序执行带有 for 循环的 Swift 4 异步调用的主要内容,如果未能解决你的问题,请参考以下文章

iOS开发:深入理解GCD 第二篇(dispatch_groupdispatch_barrier基于线程安全的多读单写)

通过 DispatchGroup 与 DispatchQueue 访问主队列

等待循环完成,DispatchGroup 使用 Swift 从 firebase 获取数据

GCD编程(封装GCD)

使用 DispatchGroup、DispatchQueue 和 DispatchSemaphore 按顺序执行带有 for 循环的 Swift 4 异步调用

如何使用 DispatchGroup 查找照片并存储在 UIImage 数组中