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

std::thread因空原生线程触发段错误问题排查求助

段错误原因分析:线程启动与对象初始化的竞态问题

问题概述

使用-O3优化参数编译代码并配合ThreadSanitizer检测时,触发零页地址写入的段错误;代码直接运行时无异常,仅在线程运行场景下出现问题。

重现代码

#include <fstream>
#include <sstream>
#include <thread>
#include <iostream>
#include <algorithm>
#include <new>
#include <thread>
#include <string>

class temp {
private:
    std::thread m_thread;
    std::vector<std::string> m_tokens;

public:
    temp()
    {
        m_thread = std::thread(&temp::run, this);
    }

    void run()
    {
        while (1) {
            std::string token;
            std::cin >> token;

            if (std::ranges::find(m_tokens, token) != m_tokens.end()) {
                std::cout << "duplicate token" << token << "\n";
                continue;
            }

            m_tokens.push_back(token);

            std::cout << token << " added\n";
        }
    }
};

int main(int argc, char **argv)
{
    temp t;
    
    while (1)
        ;
    
    return EXIT_SUCCESS;
}

错误日志

ThreadSanitizer:DEADLYSIGNAL
==19246==ERROR: ThreadSanitizer: SEGV on unknown address 0x000000000000 (pc 0x7f81d5f98880 bp 0x7b0800000020 sp 0x7f81d30bf298 T19247)
==19246==The signal is caused by a WRITE memory access.
==19246==Hint: address points to the zero page.
    #0 __tsan_func_entry <null> (libtsan.so.0+0x9e880)
    #1 std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (temp::*)(), temp*>>>::_M_run() /opt/rh/devtoolset-11/root/usr/include/c++/11/bits/std_thread.h:211 (trading+0x41e3a3)
    #2 execute_native_thread_routine <null> (trading+0x437ab3)
    #3 start_thread <null> (libpthread.so.0+0x7ea4)
    #4 clone <null> (libc.so.6+0xfeb0c)

ThreadSanitizer can not provide additional info.
SUMMARY: ThreadSanitizer: SEGV (/lib64/libtsan.so.0+0x9e880) in __tsan_func_entry
==19246==ABORTING

核心原因

  1. 对象初始化顺序与线程启动的竞态
    C++类成员的初始化顺序严格按照类中声明的顺序执行,而非构造函数中的赋值顺序。在temp类中,m_thread先于m_tokens声明,因此构造函数执行时:

    • 先完成m_thread的默认初始化
    • 接着执行m_thread = std::thread(&temp::run, this)启动线程
    • 最后才初始化m_tokens(std::vector的构造)
      线程启动后可能立即执行run方法,此时m_tokens尚未完成构造,访问这个未初始化的成员属于未定义行为,在-O3优化下,编译器的指令重排会加剧这种竞态,导致ThreadSanitizer检测到零页访问错误。
  2. ThreadSanitizer的检测放大
    直接运行时未触发错误是因为未定义行为的表现具有随机性,但ThreadSanitizer会对内存访问做严格检测,将这种潜在的非法访问暴露为明确的段错误。

解决方案

  • 延迟线程启动:不在构造函数中启动线程,新增一个单独的start方法,在对象完全构造完成后调用:
    class temp {
    private:
        std::thread m_thread;
        std::vector<std::string> m_tokens;
        bool m_running = false;
    
    public:
        temp() = default;
    
        void start() {
            if (!m_running) {
                m_thread = std::thread(&temp::run, this);
                m_running = true;
            }
        }
    
        void run() {
            while (1) {
                std::string token;
                std::cin >> token;
    
                if (std::ranges::find(m_tokens, token) != m_tokens.end()) {
                    std::cout << "duplicate token" << token << "\n";
                    continue;
                }
    
                m_tokens.push_back(token);
                std::cout << token << " added\n";
            }
        }
    };
    
    int main() {
        temp t;
        t.start(); // 对象构造完成后启动线程
        while(1);
        return 0;
    }
    
  • 调整成员声明顺序:如果一定要在构造函数中启动线程,将m_tokens的声明放在m_thread之前,确保m_tokens先完成初始化,再启动线程:
    class temp {
    private:
        std::vector<std::string> m_tokens; // 先声明,先初始化
        std::thread m_thread;
    
    public:
        temp() {
            m_thread = std::thread(&temp::run, this);
        }
    
        void run() {
            while (1) {
                std::string token;
                std::cin >> token;
    
                if (std::ranges::find(m_tokens, token) != m_tokens.end()) {
                    std::cout << "duplicate token" << token << "\n";
                    continue;
                }
    
                m_tokens.push_back(token);
                std::cout << token << " added\n";
            }
        }
    };
    
  • 添加同步机制:即使解决了初始化问题,run方法中对m_tokens的读写仍属于多线程无同步访问,会导致数据竞争,需添加std::mutex保护:
    class temp {
    private:
        std::thread m_thread;
        std::vector<std::string> m_tokens;
        std::mutex m_mutex;
    
    public:
        temp() = default;
    
        void start() {
            m_thread = std::thread(&temp::run, this);
        }
    
        void run() {
            while (1) {
                std::string token;
                std::cin >> token;
    
                std::lock_guard<std::mutex> lock(m_mutex);
                if (std::ranges::find(m_tokens, token) != m_tokens.end()) {
                    std::cout << "duplicate token" << token << "\n";
                    continue;
                }
    
                m_tokens.push_back(token);
                std::cout << token << " added\n";
            }
        }
    };
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 03:15:04