如何用模板构造函数和Lambda封装std::thread?为何func()中数据重复?
问题解答
一、为何func()中的数据会重复?
问题出在Thread构造函数的Lambda捕获逻辑上:
internal_{ [func = std::forward<Function>(f), &args...]() { func(args...); } }
这里用&args...引用捕获构造函数的参数,但构造函数的Args&&...args是转发引用,这些参数的生命周期仅存在于构造函数执行期间。当构造函数执行完毕返回后,Lambda中捕获的引用就变成了悬空引用。
在main函数的循环中,每次创建Thread对象时,构造函数的参数(比如&data[i])会被存储为临时引用,循环进入下一次迭代时,这些临时引用的内存可能被复用,导致后续线程执行时,访问的是被覆盖后的指针值,最终出现多个线程拿到相同Data地址和id的情况。
二、如何正确用模板构造函数和Lambda封装std::thread?
核心是避免悬空引用,需要将参数值捕获或完美转发到Lambda中,确保Lambda拥有参数的副本或所有权,以下是两种可行的修正方案:
方案1:用Tuple+std::apply处理参数(兼容C++17)
#include <memory> #include <thread> #include <unistd.h> #include <vector> #include <utility> #include <tuple> struct Data { int id; }; class Thread { public: template <class Function, class... Args> Thread(Function &&f, Args &&...args) noexcept : internal_{ [func = std::forward<Function>(f), args = std::make_tuple(std::forward<Args>(args)...)]() { std::apply(func, std::move(args)); } } { } ~Thread() { if (internal_.joinable()) { internal_.join(); } } private: std::thread internal_; }; void func(Data *data) { printf(" in thread, data: %p, id: %d\n", data, data->id); } int main() { static Data data[8]; std::vector<std::unique_ptr<Thread>> threads; int i = 0; for (i = 0; i < 8; i++) { data[i].id = i; printf("out thread, data: %p\n", &data[i]); threads.emplace_back(std::make_unique<Thread>(func, &data[i])); } return 0; }
方案2:直接展开参数值捕获(C++20及以上)
如果编译器支持C++20,可简化捕获逻辑:
template <class Function, class... Args> Thread(Function &&f, Args &&...args) noexcept : internal_{ [func = std::forward<Function>(f), ...args = std::forward<Args>(args)]() { func(args...); } } { }
关键修正点:
- 避免悬空引用:通过值捕获将参数的副本或移动后的所有权转移到Lambda中,确保线程执行时参数有效。
- 线程生命周期管理:添加析构函数join线程,避免程序提前退出导致线程未完成。
- 内存安全:用
std::unique_ptr管理Thread对象,避免手动new/delete引发的内存泄漏。
内容的提问来源于stack exchange,提问作者wyldavid
相关产品推荐
相关产品推荐

