The hardest part to understand the new Structured concurrency system is

Unstructured Tasks within Actors

If called from the context of an existing task:

  • if executed within the scope of a specific actor function:
    • inherit the actor's execution context and run the task on its executor, rather than the global concurrent one,
    • the closure passed to Task {} becomes actor-isolated to that actor, allowing access to the actor-isolated state, including mutable properties and non-sendable values.

Could you post me examples of using unstructured tasks within actors, as well as a thread where this part of the proposal is discussed?

As I understand it, this is mainly need for connecting UI with asynchronous code. And if we do not need to write UI at all, we can generally do without direct initialization of tasks Task{...}, but be within context of group tasks that we defined in the main file as shown here:

struct App {
  static func main() async throws {
        try! await withThrowingTaskGroup(of: Int.self) { group in
            group.addTask { await longRunningTaskClear(...) }
            group.addTask { await longRunningTaskClear(...) }

Sorry for the rambling question

You can read about this in proposals, such as swift-evolution/ at main · apple/swift-evolution · GitHub

Unstructured task can be created by simply using Task { ... } and Task.detached { ... } initializers.
Task { … } inherits actor's execution context (or no actor if unstructured task is started from a non-actor-isolated function), priority and task-local values from its surrounding context. Task.detached { ... } inherit none of those things.

Only child tasks are structured currently (up to Swift 5.7). You can create child tasks using TaskGroup api or async let for now. Later additional api might appear.

1 Like