Difference between Task {} and Task.detached() {} again.. help

Hello, I have a next code and I have an error on Task B Mutation of captured var 'z' in concurrently-executing code.

static func main() async {
    var z = 1
    let r = Task { // Task A
 
        z = 4
        return 0
    }
    let k = Task.detached() { // Task B

        z = 4 // Mutation of captured var 'z' in concurrently-executing code
        return 0
    }

    let m = await k.value
    print("n z: \(z)")
}

Please, explain, why there is no error in Task A. Because, it is also executed in parallel with the main() function and it has a concurrently-executing code🤯

3 Likes

Please, explain, why there is no error in Task A.

The main function is implicitly @MainActor (when contained in an @main type). Task A inherits the actor context of the caller, so it's @MainActor as well.

This means that the calling function and Task A are not actually executing in parallel.

The detached Task on the other hand does not inherit the actor context. It is executing in parallel and thus can't safely access z.

4 Likes

Thank you.

Please, tell

class TestController: UIViewController {
    override func viewDidLoad() {
        Task { // Task A
            return 0
        }
    }
}
class TestController: UIViewController {
    var z = 0
    
    let button = UIButton(type: .close)
    
    override func viewDidLoad() {
        view.addSubview(button)
        button.addTarget(self, action: #selector(tap), for: .touchUpInside)
    }
    
    @objc func tap() {
        Task { // Task A
            z = 4
            return 0
        }
    }
}

The 'Task A' will be executed in parallel with the main thread, will not be executed at all, or will be executed on the main thread after all operations (started by last user input) have been completed?

As you can see from its declaration UIViewController is @MainActor:

@MainActor class UIViewController : UIResponder

Both "Task A" examples inherit @MainActor from their surrounding actor context. Both tasks are executing on the main thread.

after all operations (started by last user input) have been completed?

That depends on what you call "operations":

  • Yes, currently executing non-async code will continue to execute before any other task can run on the main thread. (Tasks use cooperative multitasking.)

  • No, if the "operation" on the main thread is async, it might be interrupted at an await and your Task might run before the operation finishes.

Since your code is not async it can't be interrupted.

1 Like

And if we started our task on main thread and it is waiting a big download task now, could an user input interrupt its waiting and revive the main thread?

Not sure what you're asking but this does not block the main thread: UI is still responsive.

1 Like

To the OP,

When you define a variable in an async context. It is known as a task local variable.

That variable can only be captured if it’s sendable, or if it is being accessed from the same task context.

In this case variable z is defined in the task context of the parent function. When you create a detached task, it does not inherit that task context. So you cannot mutate variable.

In this case, you can use a regular task to solve this problem.

1 Like