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

代码触发Thread Sanitizer警告:是否为误报?求排查建议

问题分析与解决方案

代码示例

#include <thread>
#include <condition_variable>
#include <mutex>

int main()
{
    std::mutex mutex;
    bool done = false;
    std::condition_variable condvar;

    std::jthread thread([&](std::stop_token stoken)
    {
        std::this_thread::sleep_for(std::chrono::seconds(2));
        {
            std::unique_lock lock(mutex);
            done = true;
        }
        condvar.notify_all();

        while (!stoken.stop_requested())
        {
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }
    });

    std::unique_lock lock(mutex);
    condvar.wait_for(lock, std::chrono::seconds(60), [&]{ return done; });

    thread.request_stop();
}

编译命令

g++ main.cc -std=gnu++20 -fsanitize=thread

Thread Sanitizer警告分析

1. 重复锁警告(double lock)

==================
WARNING: ThreadSanitizer: double lock of a mutex (pid=2103)
    #0 pthread_mutex_lock ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:4240 (libtsan.so.0+0x53908)
    #1 __gthread_mutex_lock(pthread_mutex_t*) <null> (a.out+0x34c5)
    #2 std::mutex::lock() <null> (a.out+0x354e)
    #3 std::unique_lock<std::mutex>::lock() <null> (a.out+0x5b1f)
    #4 std::unique_lock<std::mutex>::unique_lock(std::mutex&) <null> (a.out+0x551c)
    #5 main::{lambda(std::stop_token)#1}::operator()(std::stop_token) const <null> (a.out+0x2649)
    #6 void std::__invoke_impl<void, main::{lambda(std::stop_token)#1}, std::stop_token>(std::__invoke_other, main::{lambda(std::stop_token)#1}&&, std::stop_token&&) <null> (a.out+0x3380)
    #7 std::__invoke_result<main::{lambda(std::stop_token)#1}, std::stop_token>::type std::__invoke<main::{lambda(std::stop_token)#1}, std::stop_token>(main::{lambda(std::stop_token)#1}&&, std::stop_token&&) <null> (a.out+0x32bc)
    #8 void std::thread::_Invoker<std::tuple<main::{lambda(std::stop_token)#1}, std::stop_token> >::_M_invoke<0ul, 1ul>(std::_Index_tuple<0ul, 1ul>) <null> (a.out+0x31c8)
    #9 std::thread::_Invoker<std::tuple<main::{lambda(std::stop_token)#1}, std::stop_token> >::operator()() <null> (a.out+0x3154)
    #10 std::thread::_State_impl<std::thread::_Invoker<std::tuple<main::{lambda(std::stop_token)#1}, std::stop_token> > >::_M_run() <null> (a.out+0x310a)
    #11 <null> <null> (libstdc++.so.6+0xdc252)

  Location is stack of main thread.

  Location is global '<null>' at 0x000000000000 ([stack]+0x00000001e2e0)

  Mutex M9 (0x7ffe4c7db2e0) created at:
    #0 pthread_mutex_lock ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:4240 (libtsan.so.0+0x53908)
    #1 __gthread_mutex_lock(pthread_mutex_t*) <null> (a.out+0x34c5)
    #2 std::mutex::lock() <null> (a.out+0x354e)
    #3 std::unique_lock<std::mutex>::lock() <null> (a.out+0x5b1f)
    #4 std::unique_lock<std::mutex>::unique_lock(std::mutex&) <null> (a.out+0x551c)
    #5 main <null> (a.out+0x285f)

SUMMARY: ThreadSanitizer: double lock of a mutex (/mnt/c/Users/d7d1c/Documents/Linux/test/a.out+0x34c5) in __gthread_mutex_lock(pthread_mutex_t*)
==================

这是误报。代码中主线程和子线程分别持有独立的std::unique_lock实例,对同一个std::mutex的加锁操作来自不同线程,不存在同一线程重复加锁的逻辑。误报原因大概率是TSAN对栈上分配的std::mutex跟踪存在偏差,或是g++ 11版本中libstdc++与TSAN的兼容性问题。

2. 数据竞争警告(data race)

==================
WARNING: ThreadSanitizer: data race (pid=2103)
  Write of size 1 at 0x7ffe4c7db2a3 by thread T1 (mutexes: write M9):
    #0 main::{lambda(std::stop_token)#1}::operator()(std::stop_token) const <null> (a.out+0x2669)
    #1 void std::__invoke_impl<void, main::{lambda(std::stop_token)#1}, std::stop_token>(std::__invoke_other, main::{lambda(std::stop_token)#1}&&, std::stop_token&&) <null> (a.out+0x3380)
    #2 std::__invoke_result<main::{lambda(std::stop_token)#1}, std::stop_token>::type std::__invoke<main::{lambda(std::stop_token)#1}, std::stop_token>(main::{lambda(std::stop_token)#1}&&, std::stop_token&&) <null> (a.out+0x32bc)
    #3 void std::thread::_Invoker<std::tuple<main::{lambda(std::stop_token)#1}, std::stop_token> >::_M_invoke<0ul, 1ul>(std::_Index_tuple<0ul, 1ul>) <null> (a.out+0x31c8)
    #4 std::thread::_Invoker<std::tuple<main::{lambda(std::stop_token)#1}, std::stop_token> >::operator()() <null> (a.out+0x3154)
    #5 std::thread::_State_impl<std::thread::_Invoker<std::tuple<main::{lambda(std::stop_token)#1}, std::stop_token> > >::_M_run() <null> (a.out+0x310a)
    #6 <null> <null> (libstdc++.so.6+0xdc252)

  Previous read of size 1 at 0x7ffe4c7db2a3 by main thread (mutexes: write M9):
    #0 main::{lambda()#2}::operator()() const <null> (a.out+0x2741)
    #1 bool std::condition_variable::wait_until<std::chrono::_V2::steady_clock, std::chrono::duration<long, std::ratio<1l, 1000000000l> >, main::{lambda()#2}>(std::unique_lock<std::mutex>&, std::chrono::time_point<std::chrono::_V2::steady_clock, std::chrono::duration<long, std::ratio<1l, 1000000000l> > > const&, main::{lambda()#2}) <null> (a.out+0x2c09)
    #2 bool std::condition_variable::wait_for<long, std::ratio<1l, 1l>, main::{lambda()#2}>(std::unique_lock<std::mutex>&, std::chrono::duration<long, std::ratio<1l, 1l> > const&, main::{lambda()#2}) <null> (a.out+0x2a93)
    #3 main <null> (a.out+0x28a9)

  As if synchronized via sleep:
    #0 nanosleep ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:366 (libtsan.so.0+0x6696c)
    #1 void std::this_thread::sleep_for<long, std::ratio<1l, 1l> >(std::chrono::duration<long, std::ratio<1l, 1l> > const&) <null> (a.out+0x5450)
    #2 main::{lambda(std::stop_token)#1}::operator()(std::stop_token) const <null> (a.out+0x2627)
    #3 void std::__invoke_impl<void, main::{lambda(std::stop_token)#1}, std::stop_token>(std::__invoke_other, main::{lambda(std::stop_token)#1}&&, std::stop_token&&) <null> (a.out+0x3380)
    #4 std::__invoke_result<main::{lambda(std::stop_token)#1}, std::stop_token>::type std::__invoke<main::{lambda(std::stop_token)#1}, std::stop_token>(main::{lambda(std::stop_token)#1}&&, std::stop_token&&) <null> (a.out+0x32bc)
    #5 void std::thread::_Invoker<std::tuple<main::{lambda(std::stop_token)#1}, std::stop_token> >::_M_invoke<0ul, 1ul>(std::_Index_tuple<0ul, 1ul>) <null> (a.out+0x31c8)
    #6 std::thread::_Invoker<std::tuple<main::{lambda(std::stop_token)#1}, std::stop_token> >::operator()() <null> (a.out+0x3154)
    #7 std::thread::_State_impl<std::thread::_Invoker<std::tuple<main::{lambda(std::stop_token)#1}, std::stop_token> > >::_M_run() <null> (a.out+0x310a)
    #8 <null> <null> (libstdc++.so.6+0xdc252)

  Location is stack of main thread.

  Location is global '<null>' at 0x000000000000 ([stack]+0x00000001e2a3)

  Mutex M9 (0x7ffe4c7db2e0) created at:
    #0 pthread_mutex_lock ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:4240 (libtsan.so.0+0x53908)
    #1 __gthread_mutex_lock(pthread_mutex_t*) <null> (a.out+0x34c5)
    #2 std::mutex::lock() <null> (a.out+0x354e)
    #3 std::unique_lock<std::mutex>::lock() <null> (a.out+0x5b1f)
    #4 std::unique_lock<std::mutex>::unique_lock(std::mutex&) <null> (a.out+0x551c)
    #5 main <null> (a.out+0x285f)

  Thread T1 (tid=2105, running) created by main thread at:
    #0 pthread_create ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:969 (libtsan.so.0+0x605b8)
    #1 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)()) <null> (libstdc++.so.6+0xdc328)
    #2 std::thread std::jthread::_S_create<main::{lambda(std::stop_token)#1}>(std::stop_source&, main::{lambda(std::stop_token)#1}&&) <null> (a.out+0x2b43)
    #3 std::jthread::jthread<main::{lambda(std::stop_token)#1}, , void>(main::{lambda(std::stop_token)#1}&&) <null> (a.out+0x29bb)
    #4 main <null> (a.out+0x2849)

SUMMARY: ThreadSanitizer: data race (/mnt/c/Users/d7d1c/Documents/Linux/test/a.out+0x2669) in main::{lambda(std::stop_token)#1}::operator()(std::stop_token) const
==================

这同样是误报。共享变量done的读写均受std::mutex严格保护:

  • 子线程写入done时,持有unique_lock;
  • 主线程在wait_for的谓词中读取done时,wait_for会自动重新获取锁后执行谓词逻辑。
    两者的访问不存在未同步的竞争场景。

解决方案建议

针对大型项目中的类似问题,可采取以下措施:

  • 迁移共享变量到堆:将std::mutex和done从栈内存移到堆(比如用std::make_shared包裹),避免TSAN对栈变量的跟踪误判;
  • 升级编译器版本:g++ 11的TSAN兼容性存在已知问题,升级到g++ 12或更高版本可修复部分误报;
  • 替换为原子变量:对于简单的标志变量(如done),可直接使用std::atomic<bool>替代普通bool,无需手动加锁,从根源避免竞争问题;
  • 结合其他工具排查:除TSAN外,可使用valgrind、Helgrind等工具交叉验证,或通过日志、断点手动梳理线程同步逻辑;
  • 规范同步代码:确保所有共享资源的访问都严格遵循同步规则,避免隐式的未同步操作(如捕获栈变量到线程时,需保证变量生命周期覆盖线程运行周期)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 23:42:01