我以为Combine
应该是线程安全的,尽管文档很少。我注意到,当在CurrentValueSubject
初始化线程以外的线程上发布时,某些内部会发生持续崩溃dispatch_assert_queue_fail
。
这发生在启用 的包中StrictConcurrency
。如果我禁用它,崩溃就会消失。有趣的是,当我把它放回去时,一致的崩溃不会再出现,直到我清除我的DerivedData
。
Publisher
这是预料之中的吗?如果我想要一个线程安全的解决方案来将值发布到同时保持严格的并发性,建议怎么做?
完整代码:
@preconcurrency import Combine
final class CombineTests: XCTestCase {
@MainActor
func testExample() throws {
let exp = expectation(description: "received 1")
let currentValue = CurrentValueSubject<Int, Never>(0)
let cancellable = currentValue.eraseToAnyPublisher().sink { value in
if value == 1 {
exp.fulfill()
}
}
DispatchQueue.global(qos: .background).async {
currentValue.send(1)
}
wait(for: [exp], timeout: 1)
_ = cancellable
}
}
根据此 Swift Forums 主题,从任何主题调用都是安全的。
send
这里出错的地方与此处类似。
sink
应该声明为采用@Sendable
闭包,因为可以从任何线程接收值。实际上,它并没有这样声明,因此在 Swift 6 中,在闭包的开头插入了一个检查,以检查代码是否仍在同一个全局参与者(主参与者)上运行。要解决此问题,您可以通过以下方式接收值
DispatchQueue.main
:或者您可以手动将闭包标记为
@Sendable
,这样就不会添加相同执行器检查。