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

C++安全创建线程问题:线程数控制失效引发段错误

线程数量限制失效及段错误问题解决

问题背景

在学习线程相关内容时,我在Strategy类中编写了test_function用于创建线程,原本希望通过检查已创建线程数量n_threads来限制线程总数,但该逻辑未生效,还出现了段错误。Strategy是纯虚类,需要通过clone()虚函数实现自身的动态拷贝,代码如下:

#include <ctime>
#include <mutex>
#include <thread>
#include <iostream>

class Strategy {
public:
    void test_function();
    virtual Strategy* clone() const { 
        return new Strategy(*this);
    }
private:
    void do_work(Strategy*);
    int example_counter = 0;
    inline static std::mutex __mutex;
    inline static int n_threads = 1;   
};

void Strategy::test_function() {
    clock_t __start = clock();
    for (int i = 0; i < 100; ++i) {
        Strategy* copy = clone();
        auto thread_function = [&] {
            do_work(copy);
            clock_t end = clock();
            double duration = (end - __start) / (double)CLOCKS_PER_SEC;
            std::lock_guard lock(__mutex);
            std::cout << "Thread took " << duration << "s\n";
        };
        if (n_threads < std::thread::hardware_concurrency()) {
            ++n_threads;
            std::thread perm([&] { thread_function(); --n_threads; }); 
            perm.detach();
        } else thread_function();
    }
}

void Strategy::do_work(Strategy* copy) {
    for (int i = 0; i < 100; ++i) {
        ++copy->example_counter;
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
    std::lock_guard lock(__mutex);
    example_counter = copy->example_counter;
    delete copy;
}

int main() {
    Strategy test;
    test.test_function();
    return 0;
}

问题分析

1. 线程计数的竞态条件

n_threads的读写操作没有加锁保护,多个线程同时执行++n_threads或--n_threads时会产生数据竞争,导致计数结果不准确,这就是线程数量限制逻辑失效的核心原因。

2. 引用捕获导致的悬空内存访问

  • thread_function通过[&]捕获了test_function中的局部变量__start,当test_function执行完毕返回后,该局部变量会被销毁,此时仍在运行的线程访问__start会触发未定义行为,大概率导致段错误。
  • 创建perm线程时的lambda同样使用[&]捕获,其中的thread_function是循环内的局部变量,循环迭代时thread_function会被重新定义,线程延迟执行时可能访问到已经失效的变量,同样会引发问题。

3. 纯虚类定义不符合要求

注释说明Strategy是纯虚类,但当前代码中clone()并非纯虚函数,需要修改为纯虚函数才能满足纯虚类的定义。

修复方案

1. 保护线程计数的读写操作

对n_threads的所有读写都要加锁,确保线程安全,避免竞态条件。

2. 避免引用捕获悬空变量

将需要捕获的变量改为值捕获,比如__start和copy,确保线程持有变量的副本,不会访问已销毁的内存;同时避免在lambda中捕获局部函数对象,直接内联逻辑或传递必要副本。

3. 修正纯虚类定义

将clone()声明为纯虚函数,符合Strategy作为纯虚类的要求。

修复后的代码

#include <ctime>
#include <mutex>
#include <thread>
#include <iostream>

class Strategy {
public:
    void test_function();
    // 修正为纯虚函数
    virtual Strategy* clone() const = 0;
private:
    void do_work(Strategy*);
    int example_counter = 0;
    inline static std::mutex __mutex;
    inline static int n_threads = 1;   
};

// 派生类实现纯虚函数,因为纯虚类无法直接实例化
class ConcreteStrategy : public Strategy {
public:
    Strategy* clone() const override {
        return new ConcreteStrategy(*this);
    }
};

void Strategy::test_function() {
    clock_t __start = clock();
    for (int i = 0; i < 100; ++i) {
        Strategy* copy = clone();
        // 值捕获必要变量,避免悬空
        auto thread_function = [this, __start, copy] {
            do_work(copy);
            clock_t end = clock();
            double duration = (end - __start) / static_cast<double>(CLOCKS_PER_SEC);
            std::lock_guard lock(__mutex);
            std::cout << "Thread took " << duration << "s\n";
        };

        std::lock_guard lock(__mutex);
        if (n_threads < std::thread::hardware_concurrency()) {
            ++n_threads;
            lock.unlock(); // 提前解锁,避免持有锁创建线程
            // 内联线程逻辑,避免捕获局部函数对象
            std::thread perm([this, thread_function]() mutable {
                thread_function();
                std::lock_guard inner_lock(__mutex);
                --n_threads;
            });
            perm.detach();
        } else {
            lock.unlock();
            thread_function();
        }
    }
}

void Strategy::do_work(Strategy* copy) {
    for (int i = 0; i < 100; ++i) {
        ++copy->example_counter;
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
    std::lock_guard lock(__mutex);
    example_counter = copy->example_counter;
    delete copy;
}

int main() {
    ConcreteStrategy test; // 使用派生类实例化
    test.test_function();
    // 等待所有线程完成,避免主线程提前退出
    std::lock_guard lock(Strategy::__mutex);
    while (Strategy::n_threads > 1) {
        lock.unlock();
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
        lock.lock();
    }
    return 0;
}

额外说明

  • 新增ConcreteStrategy派生类,因为纯虚类无法直接实例化,必须通过派生类实现clone()。
  • main函数中添加等待逻辑,确保所有线程完成后程序才退出,避免后台线程被强制终止。
  • 创建线程时提前释放锁,避免持有锁时间过长,提升并发效率。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 17:17:51