将临时/匿名Lambda的const引用传入std::thread构造函数是否安全?
临时/匿名Lambda的const引用传入std::thread构造函数是否安全?
结论先行:绝对不安全,会触发未定义行为,咱们来拆解下问题出在哪。
先看你给出的代码逻辑:
- main里调用
start_work时传了一个临时Lambda,这个临时对象的生命周期只到start_work函数调用完成的那一刻——因为C++里临时对象会在「完整表达式结束」时被销毁,也就是start_work执行完毕返回后,这个匿名Lambda就被销毁了。 - 而
start_work里构造std::thread时用了[&]捕获引用,这里捕获的是worker_fn的引用,而worker_fn本身就是对那个临时Lambda的const引用。
这就埋下了大隐患:
线程的构造函数会立刻返回,但线程可能不会马上执行——等它真正开始运行worker_fn()的时候,原来的临时Lambda早已经被销毁了,此时你访问的是一个悬空引用,这属于C++里的未定义行为,程序可能崩溃、输出乱码,甚至看起来“正常工作”(最危险的情况)。
怎么修复这个问题?
给你几个靠谱的方案:
1. 改用值捕获
把线程里的Lambda捕获方式从[&]改成[=],这样会把worker_fn拷贝一份到线程的Lambda内部,就算原临时Lambda销毁了,拷贝的副本还在,线程执行时访问的是这个副本,安全可靠:
template <typename Functor> void start_work(const Functor &worker_fn) { worker_thread = std::thread([=](){ worker_fn(); }); }
2. 延长临时Lambda的生命周期
在main里把匿名Lambda存成一个命名变量,再传给start_work,这样变量的生命周期会持续到main函数结束,只要你在main退出前join线程,就不会有悬空引用的问题:
int main() { auto my_worker = [](){ do_work(42); }; // 命名变量,生命周期覆盖线程执行期 start_work(my_worker); worker_thread.join(); // 必须join,避免程序退出时线程还在运行 }
3. 让start_work按值接收参数
直接把start_work的参数改成按值传递,这样函数内部会先拷贝一份Lambda,线程不管是值捕获还是引用捕获这个拷贝都安全(值捕获更稳妥):
template <typename Functor> void start_work(Functor worker_fn) // 按值传递,内部持有拷贝 { worker_thread = std::thread([=](){ worker_fn(); }); }
最后别忘了一个关键细节:不管用哪种方案,都要确保线程在程序退出前完成执行(调用join()),或者显式分离(detach()),否则程序退出时线程还在运行会触发另一种未定义行为。
内容的提问来源于stack exchange,提问作者code_fodder
相关产品推荐
相关产品推荐

