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

时间:2018-12-01 16:46:48

标签: swift grand-central-dispatch semaphore

我已经将此脚本设置为在后台循环访问一堆数据,并且成功设置了一个信号灯以使所有内容(将填充表格的数组)保持秩序,但我无法确切了解其方式或为什么信号量使数组保持顺序。进入dispatchGroup,循环停止并等待,直到下载图像为止。一旦获得图像,将dispatchSemaphore设置为1,立即退出dispatchGroup并退出信号量设置回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 个答案:

答案 0 :(得分:4)

解决方案在您的评论get image with asynchronous completion handler中。没有信号灯,所有映像下载将在同一时间开始并竞相完成,因此下载速度最快的映像将首先添加到阵列中。

因此,在开始下载后,您将立即等待信号量。这将阻塞,直到从getData方法在回调关闭中发出信号为止。只有这样循环才能继续下一个文档并下载。这样,您可以一个接一个地下载一个文件,并在下载运行时阻止当前线程。

这里不是使用串行队列的选项,因为这只会导致下载开始连续进行,但是您不能影响下载的完成顺序。

这是一个相当低效的。如果同时给多个请求,则网络层可能会运行得更快(请考虑并行下载和HTTP管道传输)。另外,您正在“浪费”一个线程,该线程可以同时执行一些其他工作。如果同时还有更多工作要做,GCD将产生另一个线程,这将浪费内存和其他资源。

一个更好的模式是跳过信号量,让下载并行运行,然后将图像直接存储在数组中的正确索引处。当然,这意味着您必须事先准备一个适当大小的数组,并且必须考虑一个占位符以防止图像丢失或出现故障。 Optionals可以很好地解决问题:

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
        }
    }
}

答案 1 :(得分:0)

DispatchGroup在这里实际上没有做任何事情。 DispatchSemaphor授予了互斥,并且该顺序仅由snapshot.documents

的迭代顺序提供