Swift运行时未检测到代码中的数据竞合并终止程序是否符合预期?
这确实是个容易让人懵的点,我来给你掰扯清楚为啥会出现这种情况~
先回看你提到的文档描述:
According to Concurrency - Swift Language Guide (emphasis mine), When you use the language-level support for concurrency, Swift detects and prevents data races, and most data races produce a compile-time error. Some data races can’t be detected until your code is running; these data races terminate code execution .
你写的这段代码里,两个Task同时对C实例的x属性执行+=1操作——别小看这个+=1,它本质是“读取当前值→加1→写入新值”的三步操作,两个任务同时执行时就很容易出现操作覆盖,最终导致c.x变成1而不是2,这确实是标准的数据竞争场景。但运行时既没终止程序,也没抛出错误,看起来和文档说的完全对不上,这到底是咋回事?
核心原因其实是:Swift的运行时数据竞争检测默认是关闭的!
文档里说的“运行时检测到数据竞争就终止程序”,是有前提的——你得手动开启这个检测功能才行。默认情况下用swift run运行代码,这个检测是不启用的,所以程序会继续跑,只是会因为数据竞争出现错误结果,而不会被终止。
如果你想让运行时检测到这个竞争并终止程序,只需要在运行时开启Thread Sanitizer(线程 sanitizer)就行,用这个命令:
swift run --sanitize=thread
用这个命令跑你的代码,只要一出现数据竞争,程序就会立刻终止,还会输出详细的竞争堆栈信息,和文档里描述的行为完全一致。
至于为啥默认不开启?因为Thread Sanitizer会带来不小的性能开销,平时开发运行程序的时候,大家更看重运行效率,所以默认是关闭的,只有在你专门排查并发问题的时候,才需要手动打开这个工具。
另外,从根源上解决这个问题的话,你可以用Swift并发框架的隔离机制,比如把C改成actor类型:
actor C { var x: Int = 0 func increment() { x += 1 } } func f(_ c: C) async { await c.increment() }
改成actor之后,Swift会在编译阶段就保证对x的访问是线程安全的,从根本上避免数据竞争的出现,连运行时检测都不需要了。
所以回到你的问题:你看到的这种未被终止的行为是完全符合预期的,因为你没有启用运行时的数据竞争检测功能。文档里的描述是在启用了检测的前提下才会触发程序终止,默认状态下程序会继续运行,只是会因为数据竞争产生错误的结果。
内容来源于stack exchange

