为何DispatchQueue.sync会引发Data race?同步执行仍告警原因
为什么同步队列任务执行完后仍出现Data Race告警?
你遇到的问题核心是对async和队列并发逻辑的误解,具体原因如下:
que1.sync的执行逻辑:que1.sync会阻塞当前线程,直到que1队列中的这个任务完全执行完毕,才会继续执行后续的que2.async代码。但que2.async只是把任务提交到que2队列,不会阻塞当前线程——提交完成后,当前线程立刻回到循环,执行下一次的que1.sync。队列并发导致的竞争:
que1和que2是两个独立的串行队列,它们的任务会被系统调度到不同线程并发执行。举个例子:第一次循环的que2.async任务还在que2队列的线程中执行时,第二次循环的que1.sync任务已经在que1队列的线程中启动了。这两个任务会同时读写porsche变量,触发数据竞争。控制台打印的误导性:
你看到que1的任务先执行完,只是线程调度的概率性结果(比如que1的任务执行速度快、qos优先级影响等),但线程调度是不确定的,TSAN(线程 sanitizer)检测到了这种潜在的并发访问风险,所以触发了告警。
解决办法
要避免数据竞争,必须保证同一时间只有一个线程访问porsche变量,常见的两种方案:
方案1:用统一的串行队列处理所有变量访问
把所有读写porsche的操作都放到同一个串行队列中,确保操作串行执行:
struct Car { var name: String } let serialQueue = DispatchQueue(label: "serial.car.access") var porsche = Car(name: "Porsche") for i in 0...100 { serialQueue.sync { porsche.name = "porsche1" print(porsche.name) porsche.name = "Porsche11" print(porsche.name) if i == 100 { print("returned ")} } serialQueue.async { porsche.name = "porsche2" print(porsche.name) porsche.name = "Porsche22" print(porsche.name) } }
方案2:用锁保护变量访问
通过加锁确保同一时间只有一个线程能读写porsche:
struct Car { var name: String } let que1 = DispatchQueue(label: "que1", qos: .background) let que2 = DispatchQueue(label: "que2", qos: .userInteractive) var porsche = Car(name: "Porsche") let lock = NSLock() for i in 0...100 { que1.sync { lock.lock() porsche.name = "porsche1" print(porsche.name) porsche.name = "Porsche11" print(porsche.name) if i == 100 { print("returned ")} lock.unlock() } que2.async { lock.lock() porsche.name = "porsche2" print(porsche.name) porsche.name = "Porsche22" print(porsche.name) lock.unlock() } }
内容的提问来源于stack exchange,提问作者Steven-Carrot
相关产品推荐
相关产品推荐

