You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何DispatchQueue.sync会引发Data race?同步执行仍告警原因

为什么同步队列任务执行完后仍出现Data Race告警?

你遇到的问题核心是对async和队列并发逻辑的误解,具体原因如下:

  1. que1.sync的执行逻辑:
    que1.sync会阻塞当前线程,直到que1队列中的这个任务完全执行完毕,才会继续执行后续的que2.async代码。但que2.async只是把任务提交到que2队列,不会阻塞当前线程——提交完成后,当前线程立刻回到循环,执行下一次的que1.sync。

  2. 队列并发导致的竞争:
    que1和que2是两个独立的串行队列,它们的任务会被系统调度到不同线程并发执行。举个例子:第一次循环的que2.async任务还在que2队列的线程中执行时,第二次循环的que1.sync任务已经在que1队列的线程中启动了。这两个任务会同时读写porsche变量,触发数据竞争。

  3. 控制台打印的误导性:
    你看到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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 17:40:31