【问题标题】:GCD: URLSession download taskGCD:URLSession 下载任务
【发布时间】:2021-01-24 15:16:09
【问题描述】:

我需要下载大量文件 - 以前一次只能下载一个文件。当前的设计是,当用户下载单个文件时,会创建一个 URLSession 任务,并使用 urlsession 的委托方法记录进度/完成/失败。我的问题是,我怎样才能在这个委托方法中留下一个调度组?我需要一次下载 10 个文件,前 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()
            })
        }
}

这甚至可能吗?如果没有,我该如何实现?

【问题讨论】:

  • 如果我要递归使用:II 会将下载数组分成最多 10 个组。然后我会创建一个DispatchGroup,在第一个块上带有enter()leave()notify()。在notify() 上,我会在块的其余部分调用方法本身。否则,(NS)Operation 也可能是一个解决方案。

标签: ios swift asynchronous grand-central-dispatch urlsession


【解决方案1】:

如果downloadAllDispatchQueue 是串行队列,您问题中的代码将死锁。当您调用wait 时,它会阻塞当前线程,直到它接收到来自另一个线程的leave 调用。如果您尝试将leave 分派到已被wait 调用阻塞的串行队列,它将死锁。

解决方案是根本不将leave 分派到队列中。没有必要这样做。直接从当前线程调用即可:

func downloadDidFinish(_ notification: Notification) {
    downloadAllDispatchGroup?.leave()
}

当下载大量文件时,我们经常使用后台会话。见Downloading Files in the Background。我们这样做是为了在用户离开应用后继续下载。

当你开始使用后台会话时,没有必要引入这种“十批次”的逻辑。后台会话为您管理所有这些请求。在“十个批次”逻辑上分层只会引入不必要的复杂性和低效率。

相反,我们只是实例化一个后台会话并提交所有请求,然后让后台会话管理来自那里的请求。它简单、高效,并且即使在用户离开应用程序后也能继续下载。如果您下载的文件太多以至于您觉得需要像这样管理它们,那么最终用户很可能会厌倦这个过程,并且可能希望在请求完成时离开应用程序去做其他事情。


【讨论】:

  • 这也不起作用。我最初尝试过这个,然后在同一个队列中添加了 leave (),想知道这是否可行。
  • 调度组是一种强大且非常可靠的机制,因此肯定有其他事情发生。也许您的调度组正在被下一个请求所取代?根据所提供的内容是不可能说的。但是调度组本身并不是问题。话虽如此,如果我试图控制下载任务的并发程度,调度组不会是我要使用的第一个工具,而是我会将它们包装在操作中,或者,如果是 iOS 13 及更高版本,仅,使用组合发布者(例如,请参阅stackoverflow.com/a/32322851/1271826)。
  • 或者,正如我所建议的,使用后台会话并完全摆脱尝试手动管理批量调度组的业务。但肯定像您在原始问题中所做的那样发送leave 是不正确的。
  • 是的,我明白你的意思,但是即使我从当前线程调用 leave() 方法,wait() 也会无限期地等待。但是,下载调用是使用后台会话完成的,我想一次将调用限制为 10 个的唯一原因是因为有一个从服务器获取下载 url 的中间调用;我不想一次超载很多电话。
  • enter/leave/wait 是健壮、可靠的方法,所以如果wait 无限期等待,这仅意味着您的所有enter 调用特定调度组尚未被同一组的关联leave 调用抵消。我建议在enterleave 调用中添加调试打印语句(例如print("enter", group)print("leave", group),并确保enter/leave 调用正确偏移。
猜你喜欢
  • 1970-01-01
  • 2017-10-22
  • 1970-01-01
  • 1970-01-01
  • 2019-03-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多