std::thread默认引用捕获引发未定义行为的原因解析
循环线程中索引打印异常的原因分析
先看出现问题的代码:
#include <iostream> #include <vector> #include <thread> int main() { std::vector<std::thread> threads; for (int i = 0; i < 3; i++) { threads.emplace_back([&](){ std::cout<<"wrong value of i = "<<i<<std::endl; }); } for (auto &thread : threads) { thread.join(); } return 0; }
问题根源:引用捕获循环变量导致的未定义行为
这段代码的核心问题是lambda通过引用捕获了循环变量i:
[&]表示引用捕获所有外部变量,lambda内部保存的是i的内存地址,而非创建线程时i的具体数值。- 主线程的循环会持续修改
i的值:从0递增到1、2,最后循环结束时i会变成3(因为i<3的条件不满足时,i已经自增到3)。 - 子线程的执行时机是不确定的——可能在主线程还在循环时执行,也可能在循环结束后才启动。当子线程执行lambda访问
i时,读取的是该内存地址的当前值,而非创建线程时的i快照,因此会打印出错误的结果(比如多次输出3,或者混合输出1、2、3等),这属于C++中的未定义行为。
为什么[&,i]能正常运行?
[&,i]的捕获规则是:默认对外部变量使用引用捕获,但显式指定i按值捕获。
- 按值捕获时,lambda会在创建线程的瞬间,复制一份当前
i的数值作为自己的内部变量。 - 后续主线程修改原
i的值,完全不会影响lambda内部的i副本。每个子线程的lambda都持有各自创建时的i副本,因此能正确打印0、1、2这三个预期值。
额外补充:如果使用C++11及以后的范围for循环(如for (int i : {0,1,2})),循环变量i在每次迭代中都是全新的变量,此时用引用捕获也不会出现问题,但传统for循环中的i是同一个变量贯穿整个循环,必须按值捕获才能避免未定义行为。
内容的提问来源于stack exchange,提问作者SolidMechanicsFan
相关产品推荐
相关产品推荐

