xcode 中的 Thread Sanitizer 给出了错误的错误

Thread Sanitizer in xcode giving wrong error

func doSomething() -> Int {
    var sum = 0
    let increaseWork = DispatchWorkItem {
        sum = sum + 100 //point 1
    }
    DispatchQueue.global().async(execute:increaseWork)
    increaseWork.wait()
    return sum //point 2
}

Thread Sanitizer 说第 1 点和第 2 点之间存在竞争条件。但我不认为存在任何竞争条件,因为 increaseWork.wait() 正在阻塞调用并且它不会通过,直到闭包被执行。

这两点之间可能存在竞争条件。想象一下如果您在 2 个单独的线程上执行 doSomething() 函数会发生什么:

  1. 第一个线程执行 increaseWork() 闭包并完成它。现在正在等待
  2. 第二个线程开始执行并命中异步执行指令
  3. 第一个线程命中 return 指令,等待可以继续
  4. 同时从第二个开始的闭包被调度执行

此时,您无法确定先执行的是什么:来自第二个线程的 sum = sum + 100 或来自第一个线程的 return sum

想法是 sum 是一个未同步的共享资源,因此,理论上,这可能是一个竞争条件。即使您注意不要发生这种情况,Thread Sanitizer 也会检测到可能的竞争条件,因为它不知道您是启动单个线程还是同时从 100 个不同的线程执行 doSomething() 函数。

更新:

由于我忽略了 sum 变量是局部的这一事实,以上解释没有回答当前的问题。所描述的场景永远不会发生在给定的代码段中。

但是,即使 sum 是一个局部变量,由于它是在闭包中使用和保留的,它将分配在堆上,而不是堆栈上。这是因为 Swift 编译器无法确定闭包是否会在 doSomething() 函数 return 之前完成执行。

为什么?因为闭包被传递给一个构造函数,它的行为类似于 @escaping 参数。这意味着无法保证何时执行闭包,因此为了安全起见,闭包保留的所有变量都必须分配在堆上。由于不知道闭包何时执行,Thread Sanitizer 无法确定 return sum 语句确实会在闭包完成后执行。

因此,即使在这里我们可以确定不会发生竞争条件,Thread Sanitizer 也会发出警报,因为它无法确定它不会发生。