Why do Task executions appear ordered inside an actor even when priorities differ?

Yes, Task {…} will create a new top-level task on behalf of the current actor (if any). Since your tasks are synchronous, they won’t run concurrently with respect to each other, but they are separate top-level tasks on behalf of MyActor.

It can. It does affect the scheduling order. As SE-0304 says :

Now, I draw your attention to key phrases, namely “may inform”, “may utilize”, and “attempt to” (emphasis added above). In my experience it tends to run them (a) in order of priority and then (b) the order that they were submitted, but neither of those are technically guaranteed. [Edit: As noted in this discussion, at least in Swift 6, a key differentiator is whether the Task closure references self or not; if you don’t do something that requires isolation, the task may not be isolated, the task itself might not be isolated and will result in the jumbled order. But once you do reference self, you will get the order of execution you expected.]

A few observations:

  1. Playgrounds are a poor platform to test performance. Build an app. Also, I might advise using an optimized “release” executable, not an unoptimized “debug” build.

  2. I might suggest doing something more than print, which runs exceedingly quickly; perhaps too quickly for the scheduler to have a chance to catch up. With your code, I saw a general preference for the high priority tasks, but not strictly so. As soon as I added something that simulated a little work (e.g., spinning in a while loop for some short period of time, say a few msec) added something that referenced self, then they ran in exactly the order one would expect.

When I did that in Xcode 16.4, I experienced the behavior one would expect: It ran the high priority tasks first, largely, if not entirely, in order, and then the low priority tasks, again in their respective order. I seem to have experienced a little more variability in this regard in the past, but that was the result of my test this evening with Swift 6.1.2.


By the way, I should make the obligatory observation that this sort of use of unstructured concurrency (neither awaiting nor handling cancellation) is obviously an anti-pattern. But as a purely educational exercise, we’re free to do what we want. Lol. Just a caveat for future readers.

3 Likes