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()
函数会发生什么:
- 第一个线程执行
increaseWork()
闭包并完成它。现在正在等待
- 第二个线程开始执行并命中异步执行指令
- 第一个线程命中 return 指令,等待可以继续
- 同时从第二个开始的闭包被调度执行
此时,您无法确定先执行的是什么:来自第二个线程的 sum = sum + 100
或来自第一个线程的 return sum
。
想法是 sum
是一个未同步的共享资源,因此,理论上,这可能是一个竞争条件。即使您注意不要发生这种情况,Thread Sanitizer 也会检测到可能的竞争条件,因为它不知道您是启动单个线程还是同时从 100 个不同的线程执行 doSomething()
函数。
更新:
由于我忽略了 sum
变量是局部的这一事实,以上解释没有回答当前的问题。所描述的场景永远不会发生在给定的代码段中。
但是,即使 sum
是一个局部变量,由于它是在闭包中使用和保留的,它将分配在堆上,而不是堆栈上。这是因为 Swift 编译器无法确定闭包是否会在 doSomething()
函数 return 之前完成执行。
为什么?因为闭包被传递给一个构造函数,它的行为类似于 @escaping
参数。这意味着无法保证何时执行闭包,因此为了安全起见,闭包保留的所有变量都必须分配在堆上。由于不知道闭包何时执行,Thread Sanitizer 无法确定 return sum
语句确实会在闭包完成后执行。
因此,即使在这里我们可以确定不会发生竞争条件,Thread Sanitizer 也会发出警报,因为它无法确定它不会发生。
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()
函数会发生什么:
- 第一个线程执行
increaseWork()
闭包并完成它。现在正在等待 - 第二个线程开始执行并命中异步执行指令
- 第一个线程命中 return 指令,等待可以继续
- 同时从第二个开始的闭包被调度执行
此时,您无法确定先执行的是什么:来自第二个线程的 sum = sum + 100
或来自第一个线程的 return sum
。
想法是 sum
是一个未同步的共享资源,因此,理论上,这可能是一个竞争条件。即使您注意不要发生这种情况,Thread Sanitizer 也会检测到可能的竞争条件,因为它不知道您是启动单个线程还是同时从 100 个不同的线程执行 doSomething()
函数。
更新:
由于我忽略了 sum
变量是局部的这一事实,以上解释没有回答当前的问题。所描述的场景永远不会发生在给定的代码段中。
但是,即使 sum
是一个局部变量,由于它是在闭包中使用和保留的,它将分配在堆上,而不是堆栈上。这是因为 Swift 编译器无法确定闭包是否会在 doSomething()
函数 return 之前完成执行。
为什么?因为闭包被传递给一个构造函数,它的行为类似于 @escaping
参数。这意味着无法保证何时执行闭包,因此为了安全起见,闭包保留的所有变量都必须分配在堆上。由于不知道闭包何时执行,Thread Sanitizer 无法确定 return sum
语句确实会在闭包完成后执行。
因此,即使在这里我们可以确定不会发生竞争条件,Thread Sanitizer 也会发出警报,因为它无法确定它不会发生。