FREE TRIAL: Accelerate your app development career with Hacking with Swift+! >>

How to voluntarily suspend a task

Paul Hudson    @twostraws   

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.

To demonstrate this, we could write a simple function to calculate the factors for a number – numbers that divide another number equally. For example, the factors for 12 are 1, 2, 3, 4, 6, and 12. A simple version of this function might look 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)
        }
    }

    return result
}

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.

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() method 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.

So, we could modify the function so that it calls Task.yield() every 100,000 numbers, like this:

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
}

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
}

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.

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. Think of this as guidance – we’re giving Swift the chance to execute other tasks temporarily rather than forcing it to do so.

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.

Hacking with Swift is sponsored by Essential Developer

SPONSORED Join a FREE crash course for iOS devs who want to become complete senior developers — from October 18th to 24th. Learn how to apply iOS app architecture patterns through a series of lectures and practical coding sessions.

Learn more

Sponsor Hacking with Swift and reach the world's largest Swift community!

BUY OUR BOOKS
Buy Pro Swift Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Coding Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift (Vapor Edition) Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Advanced iOS Volume Three Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Server-Side Swift (Kitura Edition) Buy Beyond Code

Was this page useful? Let us know!

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.