首页 > 解决方案 > swift中后台队列的性能?

问题描述

在这里跟进我的问题。

我有一个方法lotsOfWork() 可能需要一些时间才能完成。当它运行时,用户需要等待它完成。我想向用户提供反馈,以便他(她)看到正在发生的事情。

我现在有以下代码来运行我的 lotOfWork() 方法,同时允许它更新显示其进度的标签:

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var label: UILabel!

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        DispatchQueue.global(qos: .background).async {
            self.lotsOfWork()
        }
    }

    func lotsOfWork() {
        for i in 1...10 {
            DispatchQueue.main.async {
                self.label.text = "Working on item \(i)..."
            }
            sleep(1) // Simulating lots of work
        }
    }
}

但这也意味着lotsOfWork() 方法将在后台队列中执行,而不是在main 中。

当lotsOfWork() 正在运行时,主队列中没有真正发生任何事情——除了更新标签。用户只能等待。

问题 1:这是一个实质性的性能问题吗?这项工作是否需要更多时间才能完成?

问题 2:如果这是一个问题,有没有办法让 lotOfWork() 方法在 main 中运行,同时仍然能够更新 label.text

我尝试使用 DispatchQueue.main.async 甚至 2 个嵌套的 DispatchQueue.main.async,但这不会更新 label.text。

我也尝试使用 setNeedsDisplay() 和 setNeedsLayout(),但这并没有改变任何东西。

在 main 中执行lotsOfWork() 不会有问题,因为用户需要等待这项工作完成才能继续。但是如果lotsOfWork() 在main 中运行,我无法实时更新label.text。

标签: iosswiftgrand-central-dispatch

解决方案


您请求的 QoS(服务质量)不正确。您已要求background

用于非用户发起或不可见的工作。通常,用户甚至不知道这项工作正在发生。例如,预取内容、搜索索引、备份或与外部系统同步数据。

后台任务是最低优先级的任务,原则上可能永远不会执行(至少,您应该愿意接受数小时或更长时间的延迟)。

如果用户请求操作并且必须等待它完成,那么正确的 QoS 是.userInitiated

用于执行用户明确请求的工作,并且必须立即呈现结果以允许进一步的用户交互。例如,在用户在消息列表中选择电子邮件后加载它。

此 QoS 级别可以(并且通常)与主队列一样高效,尽管您通常应该避免间接猜测系统将做什么,并确保使用与意图匹配的 QoS 标记操作。对所有这一切的最佳介绍是Swift 3 中使用 GCD 进行并发编程

我一般发现当人们选择.background(最低 QoS)时,他们通常是指.utility,而当他们选择.userInteractive(最高级别)时,他们通常是指.userInitiated。最高层和最低层都有非常特殊的用例,这些用例并不经常出现。如果你需要不到一天的结果,你不是说.background,如果需要超过 16ms 才能完成,你不是说.userInteractive


推荐阅读