Actor concurrent/parallel execution

In terms of shared resources concurrency and parallelism is same thing, so question should be if " Actors serial vs concurrent execution" or " Does Actors support concurrent OR parallel execution at all"

Because I am a little confused because through all tutorials all are talking not clear. So How i now understand we can say that Actors is like Dispatch serial queue but no deterministic execution sequence.

But Apple Dev documentation says : " By default, actors execute tasks on a shared global concurrency thread pool. " so even if " Concurrency is the ability to execute multiple tasks at the same time, even if only one task makes progress at a time. " like you sad the problem with data races is actual.

So Does Actors work in global concurrent pool? or not i still does not understand. OR global concurrent pool is used but for non deterministic SERIAL QUEUE.

Actors execute concurrently in that they execute one task (really what the runtime calls a "job" I think) at a time but allow many tasks to be enqueued at once. In that regard they're like a serial queue. However, unlike a serial queue, the concurrent nature of the Swift concurrency runtime means it isn't possible to deterministically control the order in which the actor executes its jobs. For instance, while these two examples look the same, they aren't guaranteed to have the same behavior.

DispatchQueue.main.async {
  print("main 1")
}

DispatchQueue.main.async {
  print("main 2")
}

This will always print "main 1" and then "main 2".

Task.detached { @MainActor in
  print("main 1")
}

Task.detached { @MainActor in
  print("main 2")
}

While this may be likely to print "main 1" and then "main 2", that behavior is not guaranteed (it may be guaranteed if the runtime has a width of one, but I'm not sure). However, they're guaranteed to execute one at a time since they're constrained to the main actor.

This has actually changed as of SE-0431.

*Until closure isolation control is landed.

I think this is an important change, yet not available in 6.0

I thought this is still to be addressed?

Seems like that would only be true if originally started from the main actor, right? Otherwise a hop must be performed and the nondeterminism returns. I'll update to use detached to be more clear here.

I think with @MainActor it will be same answer. But with some other Actor not. So i generally I understand what do you mean. But its like same that I say. If we don't have some special isolation we could say that Actor is like Serial Dispatch Queue that use not one Thread but a pool of thread and with NON deterministic sequence of execution.

You could say it's like a serial queue targeted onto a parallel queue, but you still don't have APIs to guarantee the apparent order of enqueued work.

Or with @MainActor is Also non deterministic sequence of execution? Its Broke my understanding of Main Thread. OR not? Main thread is a serial Queue but Task guarantees this will be on Main Thread but still not necessarily consecutive? I mean if we don't use isolation.

MainActor isn't special here, using Task.detached to enqueue work onto it doesn't guarantee apparent order. It is, of course, still serial.

From the proposal text:

As a result, in the following code, these two tasks are guaranteed to start executing on the main actor in the order in which they were created, even if they immediately switch away from the main actor without having done anything that requires isolation:

func process() async {
  Task { @MainActor in
    ...
  }

  // do some work

  Task { @MainActor in
    ...
  }
}

The introduction of @isolation(any) allows the function value to dynamically carry the isolation into the Task initializer, which allows for a direct enqueue to take place without first needing an actor hop. As of 6.0 both Task.init and Task.detached have adopted @isolated(any).

2 Likes

Yeah, serial queue (if we make parallels to GCD) does not guarantee order of execution, only that one work item is executed at the time. The order (if it is important) is still has to be guaranteed separately.

Ah, great! It seems to have replaced the old @_inheritActorContext attribute (or however it was spelled), yes?

No, that attribute is still present on Task.init, just not on Task.detatched (since the latter doesn't exhibit the actor inheritance at all).

Ah, I forgot it's not visible in the generated interfaces in Xcode, just in the source.

1 Like