我尝试熟悉 Swift 并发,尤其是 MainActor。
我为自己制作了这个演示课程:
@MainActor
class ThreadsDemo {
let range1000 = 0.. < 1000
var randomNumber = 0
init() {
randomNumber = Int.random(in: range1000)
}
func modifyRandomNumber() async {
print("2. isMain: \(Thread.isMainThread)")
let newRandomNumber = Int.random(in: range1000)
print("Generated random-number -> \(newRandomNumber)")
Timer.scheduledTimer(withTimeInterval: 3.0, repeats: false) {
_ in
print("4. isMain (within wait): \(Thread.isMainThread)")
}
print("5. isMain: \(Thread.isMainThread)")
randomNumber = newRandomNumber
}
}
调用ThreadDemo方法:
.task {
print("1. isMain: \(Thread.isMainThread)")
print("Initial random-number -> \(threadsDemo.randomNumber)")
await threadsDemo.modifyRandomNumber()
print("Modified random-number -> \(threadsDemo.randomNumber)")
print("6. isMain: \(Thread.isMainThread)")
}
结果:
1. isMain: true
Initial random-number -> 455
2. isMain: true
Generated random-number -> 578
5. isMain: true
Modified random-number -> 578
6. isMain: true
4. isMain (within wait): true
显然,所有东西都在主线程上运行,甚至是预定计时器内的块。
我很困惑,因为我认为它会使用后台线程来防止 UI 冻结和无响应。
如果它无论如何都在主线程上运行所有内容,那么使用任务的目的是什么?
但我真正想弄清楚的是:
@MainActor 注释是否会导致在主线程上运行所有内容(所有可执行代码)还是只会导致始终在主线程上对状态(分配等)进行更改?
或允许您异步运行代码。它允许您调用方法和其他操作。它不必在另一个线程上。在这种情况下,您在所有操作期间调用的所有方法都
Task
恰好与主 Actor 隔离,因此它们都在主线程上运行。.task { ... }
async
await
.task { ... }
如果您碰巧调用非隔离
async
方法,或隔离到不同参与者的方法,那么该方法中的代码将在不同的线程上运行。可以说,所有代码都会改变某些状态(至少是程序计数器),因此您所说的两个选项实际上无法区分。
主 Actor 隔离方法会使其主体的所有同步
await
部分(即您不需要的部分)在主线程上运行。一旦您执行了await
某项操作,代码将在哪个线程运行取决于该“操作”。如果它恰好也与主 Actor 隔离,那么它将在主线程上运行。否则,它将不会在主线程上运行。这不仅适用于主 Actor,也适用于任何 Actor 隔离。这部分与 Swift 并发完全无关。
Timer.scheduledTimer
在当前运行循环上运行一个计时器。由于这是在主线程上调用的,因此计时器将在与主线程关联的运行循环上运行,而主线程显然会在主线程上运行。如果您使用
@MainActor
注释class
、struct
、protocol
,enum
则表示此类型只能在 MainActor(主线程)中使用。回答您的问题:是的,您在此类型中输入的任何内容都将在主线程中运行 ( https://developer.apple.com/videos/play/wwdc2021/10133 26:20)。在您的示例中,您的方法
modifyRandomNumber
将始终在主线程上运行,不会出现异常。Task
继承 Actor 隔离,这意味着在你的类中,每个都Task
将在主线程上运行如果你不想为你的任务继承 Actor 隔离并在不同的线程上运行代码,请使用
Task.detached
如果你想打破某些方法的参与者隔离,MainActor(或某些参与者)类型的属性,你可以将方法/属性注释为非隔离