swift actors是并发的,但不是并行的?



我想弄清楚,演员带来了什么。我不清楚它们是真正的并行还是仅仅是并发。

我做了一个小测试来检查自己:

actor SomeActor {
func check() {
print("before sleep")
sleep(5)
print("after sleep")
}
}
let act1 = SomeActor()
let act2 = SomeActor()
Task {
await withTaskGroup(of: Void.self) { group in
group.addTask {
await act1.check()
}
group.addTask {
await act2.check()
}
}
}

输出是:

before sleep // Waiting 5 sec
after sleep
before sleep // Starting after 5 sec
after sleep

我阻塞当前执行线程,所以它不会产生它。但是第二个任务不能并行启动。

那么这是否意味着相同actor类型的每个实例共享executor实例呢?

如果是这样,那么同一个actor的多个实例不支持真正的并行,但并发执行?

乌利希期刊指南:

我有一些新的观察。因此,当我们运行group.add()时,上面的例子使用继承隔离。所以它不能立即传递第二个actor调用。

我的第二个观察-当我们以这种方式运行任务时:

let act1 = SomeActor()
let act2 = SomeActor()

Task.detached {
await act1.check()
}
Task { @MainActor in
await act2.check()
}

输出是:

before sleep
before sleep
after sleep
after sleep

所以它确实是平行的

但是当我使用分离任务时,输出是串行的:

let act1 = SomeActor()
let act2 = SomeActor()

Task.detach {
await act1.check()
}
Task.detached { @MainActor in
await act2.check()
}

输出是:

before sleep
after sleep
before sleep
after sleep

如果您添加一些额外的调试,可能会有所帮助,如:

actor SomeActor {
var name: String
init(name: String) {
self.name = name
}
func check() {
print("Actor: (name) check(), sleeping now, blocking thread (Thread.current)")
sleep(1)
print("Actor: (name), sleep done, unblocking thread (Thread.current)")
}
}
let act1 = SomeActor(name: "A")
let act2 = SomeActor(name: "B")
Task() {
print("in task, on thread: (Thread.current)")
await withTaskGroup(of: Void.self) { group in
group.addTask {
print("Group task: 1 (on thread: (Thread.current)")
await act1.check()
}
group.addTask {
print("Group task: 2 (on thread: (Thread.current)")
await act2.check()
}
}
}

Task就像调度到一个全局队列,其优先级从Task中获取。init的priority:参数

如果你阻塞任务正在使用的线程,使用sleep(不要这样做),这不是产生线程。如果你使用异步睡眠,尝试?等待的任务。睡觉之后你会看到合作行为:

actor SomeActor {
var name: String
init(name: String) {
self.name = name
}
func check() async throws {
print("Actor: (name) check(), sleeping now, yielding thread (Thread.current)")
try await Task.sleep(nanoseconds: NSEC_PER_SEC)
print("Actor: (name), Task.sleep done (was not cancelled), back now on thread (Thread.current)")
}
}
let act1 = SomeActor(name: "A")
let act2 = SomeActor(name: "B")
Task() {
print("in task, on thread: (Thread.current)")
await withTaskGroup(of: Void.self) { group in
group.addTask {
print("Group task: 1 (on thread: (Thread.current)")
try? await act1.check()
}
group.addTask {
print("Group task: 2 (on thread: (Thread.current)")
try? await act2.check()
}
}
}

给出如下输出:

in task, on thread: <NSThread: ...>{number = 4, name = (null)}
Group task: 1 (on thread: <NSThread:...>{number = 4, name = (null)}
Actor: A check(), sleeping now, yielding thread <NSThread: ...>{number = 4, name = (null)}
Group task: 2 (on thread: <NSThread: ...>{number = 4, name = (null)}
Actor: B check(), sleeping now, yielding thread <NSThread: ...>{number = 4, name = (null)}
Actor: A, Task.sleep done (was not cancelled), back now on thread <NSThread: ...>{number = 7, name = (null)}
Actor: B, Task.sleep done (was not cancelled), back now on thread <NSThread: ...>{number = 7, name = (null)}

回答您的问题,独立的参与者实例可以并行运行(只要协作线程池不受约束)。当我运行您的代码片段时,我喜欢并行执行,因此我怀疑您的测试中有其他方面产生了串行行为。我们需要MCVE来进一步诊断。

例如,如果您在iOS模拟器上运行此程序,则会人为地限制协作线程池。请参阅async-await任务组的最大线程数。虽然我无法证明你所描述的行为,但模拟器的受限合作线程池很容易导致人们对潜在的并行性做出错误的推断。

如果您在模拟器上运行此程序,请尝试在物理设备或macOS目标上进行测试,您将享受到一个协作线程池,它将利用设备上所有可用的内核。

最新更新