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
相关产品推荐
相关产品推荐

