代码之家  ›  专栏  ›  技术社区  ›  youareawaitress

信号量如何保持异步循环的顺序?

  •  1
  • youareawaitress  · 技术社区  · 6 年前

    我已经设置了这个脚本来在后台循环访问大量数据,并且成功地设置了一个信号量来保持所有内容(将填充表的数组)的顺序,但是我不能确切地理解信号量如何或为什么保持数组的顺序。这个 dispatchGroup 进入后,循环停止并等待图像下载,一旦图像 dispatchSemaphore 设置为 1 然后马上 调度组 退出并将信号量设置回 0 . 信号量从0切换到1的速度太快,我不明白它是如何保持数组的顺序的。

    let dispatchQueue = DispatchQueue(label: "someTask")
    let dispatchGroup = DispatchGroup()
    let dispatchSemaphore = DispatchSemaphore(value: 0)
    
    dispatchQueue.async {
    
        for doc in snapshot.documents {
    
            // create data object for array
    
            dispatchGroup.enter()
    
            // get image with asynchronous completion handler
            Storage.storage().reference(forURL: imageId).getData(maxSize: 1048576, completion: { (data, error) in
    
                defer {
                    dispatchSemaphore.signal()
                    dispatchGroup.leave()
                }
    
                if let imageData = data,
                    error == nil {
                    // add image to data object
                    // append to array
                }
    
            })
    
            dispatchSemaphore.wait()
    
        }
    
        // do some extra stuff in background after loop is done
    
    }
    
    dispatchGroup.notify(queue: dispatchQueue) {
    
        DispatchQueue.main.async {
            self.tableView.reloadData()
        }
    
    }
    
    2 回复  |  直到 6 年前
        1
  •  3
  •   Sven    6 年前

    解决方案在您的评论中 get image with asynchronous completion handler . 如果没有信号灯,所有的图像下载都将同时开始并竞争完成,因此下载速度最快的图像将首先添加到数组中。

    所以在你开始下载之后,你马上等待你的信号灯。这将一直阻塞,直到在getdata方法的回调关闭中发出信号为止。只有这样,循环才能继续到下一个文档并下载它。通过这种方式,您可以逐个下载一个文件,并在下载运行时阻止当前线程。

    使用串行队列不是这里的一个选项,因为这只会导致下载 开始 但您可以影响它们的完成顺序。

    不过,这是一种相当低效的做法。如果同时向网络层发出多个请求,那么它的运行速度可能会更快(考虑并行下载和HTTP管道)。同时,你也在浪费一条可以同时做一些不同工作的线。如果在同一时间还有更多的工作要做,那么GCD将生成另一个线程,这会浪费内存和其他资源。

    更好的模式是跳过信号量,让下载并行运行,并将图像直接存储在数组中的正确索引中。当然,这意味着您必须事先准备一个适当大小的数组,并且您必须为丢失或失败的图像考虑一个占位符。期权可以很好地发挥作用:

    var images: [UIImage?] = Array(repeating: nil, count: snapshot.documents.count)
    
    for (index, doc) in snapshot.documents.enumerated() {
    
        // create data object for array
    
        dispatchGroup.enter()
    
        // get image with asynchronous completion handler
        Storage.storage().reference(forURL: imageId).getData(maxSize: 1048576) { data, error in
    
            defer {
                dispatchGroup.leave()
            }
    
            if let imageData = data,
                error == nil {
                // add image to data object
                images[index] = image
            }
        }
    }
    
        2
  •  0
  •   Alexander    6 年前

    这个 DispatchGroup 在这里什么都没做。你被允许相互排斥 DispatchSemaphor ,并且顺序简单地由 snapshot.documents