在 XCode 8.3 上使用 Swift 3.1,使用 Thread Sanitizer 运行以下代码会发现数据竞争(请参阅代码中的写入和读取注释):
private func incrementAsync() {
let item = DispatchWorkItem { [weak self] in
guard let strongSelf = self else { return }
strongSelf.x += 1 // <--- the write
// Uncomment following line and there's no race, probably because print introduces a barrier
//print("> DispatchWorkItem done")
}
item.notify(queue: .main) { [weak self] in
guard let strongSelf = self else { return }
print("> \(strongSelf.x)") // <--- the read
}
DispatchQueue.global(qos: .background).async(execute: item)
}
这对我来说似乎很奇怪,因为DispatchWorkItem
它允许提及的文档:
收到有关他们完成的通知
这意味着notify
一旦工作项的执行完成,就会调用回调。
所以我希望's 的工作关闭和它的通知关闭之间会有happens-before
关系。DispatchWorkItem
将 aDispatchWorkItem
与这样的注册notify
回调一起使用不会触发 Thread Sanitizer 错误的正确方法是什么(如果有的话)?
我尝试注册notify
withitem.notify(flags: .barrier, queue: .main) ...
但比赛仍然存在(可能是因为该标志仅适用于同一个队列,有关该标志的作用的文档很少.barrier
)。但是,即使在与工作项的执行相同的(后台)队列上调用 notify ,也会flags: .barrier
导致竞争。
如果你想试试这个,我在 github 上发布了完整的 XCode 项目:https ://github.com/mna/TestDispatchNotify
有一个TestDispatchNotify
方案可以在没有 tsan 的情况下构建应用程序,并TestDispatchNotify+Tsan
激活 Thread Sanitizer。
谢谢,马丁