How to voluntarily suspend a task  如何自愿暂停任务

How to voluntarily suspend a task 如何自愿暂停任务

温馨提示:本文最后更新于2025-02-08 12:38:44,某些文章具有时效性,若有错误或已失效,请在下方留言

If you’re executing a long-running task that has few if any suspension points, for example if you’re repeatedly iterating over an intensive loop, you can call Task.yield() to voluntarily suspend the current task so that Swift can give other tasks the chance to proceed a little if needed.
如果您正在执行一个长时间运行的任务,该任务几乎没有暂停点(如果有的话),例如,如果您正在重复迭代一个密集的循环,您可以调用 Task.yield() 来自愿暂停当前任务,以便 Swift 可以在需要时给其他任务机会继续进行一些作。

To demonstrate this, we could write a simple function to calculate the factors for a number – numbers that divide another number evenly. For example, the factors for 12 are 1, 2, 3, 4, 6, and 12. A simple version of this function might look like this:
为了证明这一点,我们可以编写一个简单的函数来计算一个数字的因数——将另一个数字整除的数字。例如,12 的因子为 1、2、3、4、6 和 12。此函数的简单版本可能如下所示:

func factors(for number: Int) async -> [Int] {
    var result = [Int]()

    for check in 1...number {
        if number.isMultiple(of: check) {
            result.append(check)
        }
    }

    return result
}

let results = await factors(for: 120)
print("Found \(results.count) factors for 120.")
下载图标
how-to-voluntarily-suspend-a-task-1.zip
zip文件
49.9K

Despite being a pretty inefficient implementation, in release builds that will still execute quite fast even for numbers such as 100,000,000. But if you try something even bigger you’ll notice it struggles – running hundreds of millions of checks is really going to make the task chew up a lot of CPU time, which might mean other tasks are left sitting around unable to make even the slightest progress forward.
尽管这是一个非常低效的实现,但在发布版本中,即使对于像 100,000,000 这样的数字,它仍然会执行得相当快。但是,如果您尝试更大的方法,您会注意到它很挣扎 – 运行数亿次检查确实会使任务消耗大量 CPU 时间,这可能意味着其他任务无法取得哪怕是最轻微的进展。

Keep in mind our other tasks might be able to kick off some work then suspend immediately, such as making network requests. A simple improvement is to force our factors() factor to pause every so often so that Swift can run other tasks if it wants – we’re effectively asking it to come up for air and let another task have a go.
请记住,我们的其他任务可能能够启动一些工作然后立即暂停,例如发出网络请求。一个简单的改进是强制我们的 factors() 因子每隔一段时间暂停一次,这样 Swift 就可以在需要时运行其他任务 —— 我们实际上是在请求它来呼吸,让另一个任务试一试。

So, we could modify the function so that it calls Task.yield() every 100,000 numbers, like this:
因此,我们可以修改函数,使其每 100,000 个数字调用 Task.yield(),如下所示:

func factors(for number: Int) async -> [Int] {
    var result = [Int]()

    for check in 1...number {
        if check.isMultiple(of: 100_000) {
            await Task.yield()
        }

        if number.isMultiple(of: check) {
            result.append(check)
        }
    }

    return result
}

let factors = await factors(for: 120)
print("Found \(factors.count) factors for 120.")
下载图标
how-to-voluntarily-suspend-a-task-2.zip
zip文件
49.9K

However, that has the downside of now having twice as much work in the loop. As an alternative, you could try yielding only when a multiple is actually found, like this:
但是,这样做的缺点是现在循环中的工作量增加了一倍。作为替代方案,您可以尝试仅在实际找到倍数时才让步,如下所示:

func factors(for number: Int) async -> [Int] {
    var result = [Int]()

    for check in 1...number {   
        if number.isMultiple(of: check) {
            result.append(check)
            await Task.yield()                
        }
    }

    return result
}

let factors = await factors(for: 120)
print("Found \(factors.count) factors for 120.")
下载图标
how-to-voluntarily-suspend-a-task-3.zip
zip文件
49.9K

That offers Swift the chance to pause every time a multiple is found. Yes, it will be called a lot in the first few iterations, but fewer multiples will be found over time and so it probably won’t yield as often as the previous example – it could well defeat the point of using yield() in the first place.
这为 Swift 提供了每次找到倍数时暂停的机会。是的,它在前几次迭代中会被调用很多,但随着时间的推移会发现更少的倍数,因此它可能不会像前面的例子那样经常产生——它很可能首先破坏了使用 yield() 的意义。

Calling yield() does not always mean the task will stop running: if it has a higher priority than other tasks that are waiting, it’s entirely possible your task will just immediately resume its work. It’s guidance – we’re giving Swift the opportunity to execute other tasks temporarily rather than forcing it to do so.
调用 yield() 并不总是意味着任务将停止运行: 如果它的优先级高于正在等待的其他任务,则您的任务完全有可能立即恢复其工作。 这是指导 – 我们给 Swift 临时执行其他任务的机会,而不是强迫它这样做。

Think of calling Task.yield() as the equivalent of calling a fictional Task.doNothing() method – it gives Swift the chance to adjust the execution of its tasks without actually creating any real work.
将调用 Task.yield() 看作等同于调用虚构的 Task.doNothing() 方法——它让 Swift 有机会调整其任务的执行,而无需实际创建任何实际工作。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享