0
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() 正在阻塞调用,并且在执行闭包之前它不会通过。

4

1 回答 1

0

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

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

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

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

更新:

由于我错过了变量是本地的这一事实,sum因此上述解释并未回答当前问题。所描述的场景永远不会在给定的代码中发生。

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

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

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

于 2020-04-15T07:58:51.433 回答