如何让Clang ThreadSanitizer忽略已检测到的数据竞争?
问题解答
一、你的想法是否正确?
在你明确接受UI偶尔显示无效/旧值的前提下,这个想法是合理的。从标准C++角度,数据竞争属于未定义行为——编译器可能因为检测到无同步的读写,做出把sharedValue缓存到寄存器、重排指令等优化,导致UI线程读到完全出乎意料的值。但在调试UI这种对数据一致性要求极低的场景,如果你能接受偶尔的异常显示,确实没必要为了这点同步付出额外的运行时开销和开发成本。
二、如何让Clang的ThreadSanitizer忽略该数据竞争?
有两种常用方法:
- 直接标记代码:用
__attribute__((no_sanitize("thread")))修饰涉及读写的函数或变量。比如给UI线程函数加标记:
或者给共享变量加标记:__attribute__((no_sanitize("thread"))) void uiThread() { while (window.open()) { window.display(sharedValue); } }__attribute__((no_sanitize("thread"))) double sharedValue = 0.0; - 使用抑制文件:创建一个名为
.tsan-suppressions的文件,内容如下:
编译运行时添加参数:race:sharedValue-fsanitize=thread -fsanitize-suppressions=./.tsan-suppressions
三、x86架构下的具体行为
x86采用强内存模型(除了movnt等非临时存储指令),对于对齐的64位double变量:
- 读写操作是原子的:x86的
movq指令会一次性完成64位数据的读写,不会出现读取到半更新的“损坏”数值——UI线程读到的要么是逻辑线程更新前的完整旧值,要么是更新后的完整新值。 - 但
sharedValue += 0.333是读-修改-写的复合操作,这个过程本身不是原子的——逻辑线程可能在读取、计算、写入的中间被UI线程打断,但UI线程读到的依然是某个完整的数值,不会出现无效的位模式。
注意:这只是x86硬件的特性,标准C++并没有做任何保证,换做ARM等弱内存模型架构,情况可能完全不同。
示例代码
double sharedValue = 0.0; void logicThread() { while (true) { sharedValue += 0.333; } } void uiThread() { while (window.open()) { window.display(sharedValue); } }
内容的提问来源于stack exchange,提问作者chrysante
相关产品推荐
相关产品推荐

