问题描述
我需要下载大量文件-以前一次只能下载一个文件。当前的设计是,当用户下载单个文件时,将创建URLSession任务,并使用urlsession的委托方法记录进度/完成/失败。我的问题是,如何在此委托方法中保留调度组?我需要一次下载10个文件,在前十个文件完成后再开始下一个10个文件。现在,如果我将派遣组保留在委托方法中,则派遣组的等待将永远等待。到目前为止,这是我已经实现的:
self.downloadAlldispatchQueue.async(execute: {
self.downloadAlldispatchGroup = dispatchGroup()
let maximumConcurrentDownloads: Int = 10
var concurrentDownloads = 0
for i in 0..<files.count
{
if self.cancelDownloadAll {
return
}
if concurrentDownloads >= maximumConcurrentDownloads{
self.downloadAlldispatchGroup.wait()
concurrentDownloads = 0
}
if let workVariantPart = libraryWorkVariantParts[i].workVariantPart {
concurrentDownloads += 1
self.downloadAlldispatchGroup.enter()
//call method for download
}
}
self.downloadAlldispatchGroup!.notify(queue: self.downloadAlldispatchQueue,execute: {
dispatchQueue.main.async {
}
})
})
在代表中:
func downloadDidFinish(_ notification: Notification){
if let dispatchGroup = self.downloadAlldispatchGroup {
self.downloadAlldispatchQueue.async(execute: {
dispatchGroup.leave()
})
}
}
这甚至可能吗?如果没有,我该如何实现?
解决方法
如果downloadAllDispatchQueue
是串行队列,则您问题中的代码将死锁。当您调用wait
时,它将阻塞该当前线程,直到它从另一个线程接收到leave
调用为止。如果您尝试将leave
分派到已经被wait
调用阻塞的串行队列中,它将死锁。
解决方案是根本不将leave
分派到队列。没有必要。只需直接从当前线程中调用它即可:
func downloadDidFinish(_ notification: Notification) {
downloadAllDispatchGroup?.leave()
}
下载大量文件时,我们经常使用后台会话。参见Downloading Files in the Background。我们这样做是为了使下载即使在用户离开应用程序后仍会继续。
当您开始使用后台会话时,无需介绍这种“十批”逻辑。后台会话为您管理所有这些请求。在“十批”逻辑上分层只会带来不必要的复杂性和低效率。
相反,我们只实例化一个后台会话并提交所有请求,然后让后台会话从那里管理请求。它简单,高效,即使在用户离开应用程序后,也可以继续下载。如果下载的文件太多,以至于您需要像这样来管理它们,则最终用户很可能会厌倦此过程,并可能希望在请求完成时离开应用程序来执行其他操作。