You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何ThreadSanitizer仅报告单个数据竞争?测试程序含两处竞态

问题:ThreadSanitizer仅报告一处数据竞争的疑问

我编写了一个故意包含两处数据竞争的测试程序,但无论如何修改,ThreadSanitizer(TSAN)始终仅报告第一处竞态。请问TSAN是否只会报告单个Bug?是否存在Bug被其他Bug遮挡导致无法检测的情况?这与我的程序是否有关?

测试程序代码

#include <thread>
#include <mutex>
#include <atomic>
#include <iostream>
#include <unistd.h>

std::atomic<bool> stop{false};

std::mutex main_t1_mutex;
std::mutex t1_t2_mutex; // fully useless by intention

// global vars accessed by main and threads
int i1; 
int i2; 

void func_t1(int* retval)
{
    int internal = 0;
    //while( !stop )
    for ( unsigned int tt = 0; tt < 1000; tt++ )
    {   
        //main_t1_mutex.lock();
        t1_t2_mutex.lock();
        internal+=i1;
        t1_t2_mutex.unlock();
        //main_t1_mutex.unlock();
        usleep(1*100);
    }   

    *retval=internal;
}

void func_t2(int* retval)
{
    int internal = 0;
    //while( !stop )
    for ( unsigned int tt = 0; tt < 1000; tt++ )
    {   
        // this mutex will never protect against main thread. Intended for testing valgrind/tsan
        t1_t2_mutex.lock();
        internal+=i2;
        t1_t2_mutex.unlock();
        usleep(1*100);
    }   

    *retval = internal;
}

int main()
{
    std::cout << "Hallo" << std::endl;

    int retval1;
    int retval2;
    std::thread t1( func_t1, &retval1 );
    std::thread t2( func_t2, &retval2 );

    for ( unsigned int i = 0; i < 10; i++ )
    {   
        usleep(1*1000);
        main_t1_mutex.lock();
        i1 = i;
        i2 = i;
        main_t1_mutex.unlock();
        std::cout << "step " << i << std::endl;
    }

    //stop=true;

    t1.join();
    t2.join();

    std::cout << "Value 1: " << retval1 << std::endl;
    std::cout << "Value 2: " << retval2 << std::endl;
}

解答

TSAN不是只会报告单个数据竞争,它具备检测并报告多个竞态条件的能力,但确实存在部分竞态因未被实际触发而无法被检测到的情况,这和你的程序逻辑直接相关。

你的程序中的两处竞态分析

  1. 第一处(已被检测):i1的访问冲突。主线程用main_t1_mutex保护i1的写操作,但t1线程用t1_t2_mutex读取i1——两个线程使用的是不同互斥锁,完全无法同步,因此必然存在数据竞争,TSAN能稳定捕捉到这个冲突。
  2. 第二处(未被检测):i2的访问冲突。主线程用main_t1_mutex写i2,t2线程用t1_t2_mutex读i2,同样是不同锁的无同步访问,理论上也是数据竞争,但TSAN没报告的原因在于:
    • 竞态触发概率低:主线程仅循环10次,每次休眠1秒;而t2线程循环1000次,每次休眠0.1秒。两者的访问时机交叉概率极低,TSAN需要捕捉到实际发生的线程间数据访问冲突才能报告,若运行过程中没产生足够的交叉操作,就不会被检测到。
    • 线程调度随机性:不同运行环境下的线程调度顺序不同,可能某次运行能触发第二处竞态,某次则不行。
    • TSAN的动态检测特性:TSAN是动态工具,依赖程序运行时的实际执行轨迹,而非静态分析。如果某个竞态在本次运行中没有实际发生,就不会被记录。

让TSAN检测到第二处竞态的调整方案

  • 缩短主线程的休眠时间,比如把usleep(1*1000)改为usleep(100),提高写i2的频率,增加和t2线程读操作的交叉概率。
  • 移除所有usleep调用,让线程尽可能密集地访问共享变量,大幅提升竞态触发的可能性。
  • 增加主线程的循环次数,比如从10改为1000,延长写操作的持续时间,覆盖t2线程的整个执行周期。

补充说明

TSAN默认会在检测到第一个数据竞争后继续运行(除非你通过halt_on_error=1参数强制终止),所以它并非只报告单个Bug,只是你的程序中第二处竞态未被实际触发而已。


内容的提问来源于stack exchange,提问作者Klaus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 09:20:16