不调用std::future::get()时下述C++代码是否存在潜在问题?
问题:不调用
std::future::get()是否存在代码风险 如果不调用std::future::get(),下方代码片段是否存在潜在问题?
我进行了多次测试,发现即使std::async启动的任务需要耗费较长时间才能执行完毕,不调用std::future::get()时上述代码似乎也能正常运行,这一结果完全超出我的预期。
#include<future> #include<iostream> #include<array> #include<algorithm> #include<thread> #include<vector> std::array<int, 100000> arr; int sum=0; struct Wrapper { void consume() { std::cout << "consumer:" << std::this_thread::get_id() << std::endl; std::for_each(arr.begin(), arr.end(), [](int val) {sum+=val; }); } void produce() { std::cout << "producer:" <<std::this_thread::get_id() << std::endl; int a=0; while(true) { if(a++>1e9) { break; } } } }; int main() { std::fill(arr.begin(), arr.end(), 1); std::cout << "main:" <<std::this_thread::get_id() << std::endl; Wrapper wrap; std::vector<std::future<void>> vec; vec.push_back(std::async(std::launch::async, &Wrapper::produce, &wrap)); vec.push_back(std::async(std::launch::async, &Wrapper::consume, &wrap)); #ifdef WAIT //Is there any potencial problem if the block below does not run? for(auto& future:vec) { future.get(); } #endif }
解答
你测试时看到的“不调用get也正常运行”不是巧合,是C++标准给std::async返回的std::future加的特殊规则:这类future在析构时会阻塞,直到关联的异步任务执行完成。你代码里的vec是main函数内的局部变量,main退出前会先析构vec,vec内部存储的两个future会依次析构,自动等待produce和consume两个任务跑完,所以不会出现任务没跑完程序就退出的情况。
但这种写法依然存在不少潜在问题:
- 阻塞位置完全不可控:你对析构阶段的隐式阻塞没有感知,如果后续在main函数末尾、vec析构之前加了需要及时执行的逻辑(比如上报运行结果、释放外部句柄、输出收尾日志),这些逻辑的执行时机不会符合你的预期;如果异步任务出现死锁、无限循环,程序会直接卡死在变量析构阶段,问题排查难度极高。
- 隐式等待行为不具备通用性:只有
std::async返回的、绑定异步运行任务的future才有析构阻塞的特性,如果你后续修改代码,把启动策略改成std::launch::deferred,或者future是从std::promise、std::packaged_task拿到的,future析构时不会做任何等待,程序会直接退出,异步任务可能执行到一半被强制终止,引发数据损坏、崩溃等问题。 - 代码本身还有独立于future调用的并发问题:全局变量
sum在consume线程中被修改,全程没有加锁或者原子保护,属于多线程数据竞争,是标准定义的未定义行为,你测试时运行正常只是运气好,换编译选项、换平台、换硬件都可能出现计算结果错误、程序崩溃的问题。
实际开发中不要依赖future析构的隐式等待,显式调用get()或者wait()可以明确标注等待的位置,代码可读性和可维护性都更好。
内容的提问来源于stack exchange,提问作者John
相关产品推荐
相关产品推荐

