Consider the following, relatively simple Swift program:
import Foundation
func printContext(function: String = #function, line: Int = #line) {
print("At \(function):\(line): Running on \(Thread.current) (main: \(Thread.isMainThread))")
}
printContext()
Task { @MainActor in
printContext()
}
Task.detached { @MainActor in
printContext()
}
Task {
await MainActor.run {
printContext()
}
}
DispatchQueue.main.async {
printContext()
}
dispatchMain()
According to the global actor proposal, I would expect DispatchQueue.main.async { ...
to be roughly equivalent to Task.detached { @MainActor in ...
.
Yet with Swift 5.6.1 on arm64-apple-macosx12.0
, the program seems to nondeterministically yield different results upon invocation. Sometimes I get the expected output:
At main:7: Running on <_NSMainThread: 0x600000083c80>{number = 1, name = main} (main: true)
At main:10: Running on <_NSMainThread: 0x600000083c80>{number = 1, name = main} (main: true)
At main:19: Running on <_NSMainThread: 0x600000083c80>{number = 1, name = main} (main: true)
At main:14: Running on <_NSMainThread: 0x600000083c80>{number = 1, name = main} (main: true)
At main:24: Running on <_NSMainThread: 0x600000083c80>{number = 1, name = main} (main: true)
Sometimes the @MainActor
closures seem to execute on another thread:
At main:7: Running on <_NSMainThread: 0x600002ae44c0>{number = 1, name = main} (main: true)
At main:24: Running on <_NSMainThread: 0x600002ae44c0>{number = 1, name = main} (main: true)
At main:10: Running on <NSThread: 0x600002afff00>{number = 2, name = (null)} (main: false)
At main:19: Running on <NSThread: 0x600002afff00>{number = 2, name = (null)} (main: false)
At main:14: Running on <NSThread: 0x600002afff00>{number = 2, name = (null)} (main: false)
Only the DispatchQueue
mechanism seems to reliably schedule onto the main
thread. Am I misunderstanding part of the concurrency model or why does the program behave this way?