C++同步函数内调用std::async:为何两种场景执行流程不同?
先看你的两个测试场景:
场景1:所有函数异步调用
代码如下:
void threadFunc() { while (1) { std::cout << "thread working..\n"; std::this_thread::sleep_for(1s); } } void middleFunc() { std::async(std::launch::async, threadFunc); std::cout << "humble\n"; } int main() { std::async(std::launch::async, middleFunc); // 异步调用middleFunc while (1) { std::cout << "main working..\n"; std::this_thread::sleep_for(1s); } }
输出:
main working.. humble thread working.. main working.. thread working.. ...
场景2:middleFunc同步调用
修改main函数为同步调用:
int main() { middleFunc(); // 同步调用middleFunc while (1) { std::cout << "main working..\n"; std::this_thread::sleep_for(1s); } }
输出:
humble thread working.. thread working.. ...
永远看不到main working..的输出,说明middleFunc没有返回,main无法进入后续循环。
问题的核心:std::future临时对象的析构阻塞行为
这个坑很多C++开发者都踩过——当你用std::launch::async策略调用std::async,却不保存返回的std::future对象时,临时std::future的析构函数会强制阻塞当前线程,直到异步任务完成。
这是C++标准明确规定的:对于std::launch::async创建的异步任务,其返回的std::future的析构函数会像std::thread::join()一样,等待线程执行完毕才会继续。
现在对应两个场景拆解:
场景1为什么正常?
main里用std::async异步调用middleFunc,这意味着middleFunc是在独立的子线程中运行的。middleFunc里的std::async创建的临时std::future,会在middleFunc的子线程中触发析构阻塞——这个子线程会一直等threadFunc(无限循环),但main线程完全不受影响,因为它只是启动了middleFunc的异步任务,没有等待其完成,所以main可以直接进入自己的循环输出。
场景2为什么无法返回?
main是同步调用middleFunc,所以middleFunc在main线程中执行。middleFunc里的std::async语句执行完毕后,临时std::future立即析构,触发main线程阻塞,等待threadFunc完成。但threadFunc是无限循环,永远不会结束,所以main线程被死死卡在middleFunc的执行流程中,永远无法进入后续的while循环。
怎么解决这个问题?
如果不想让std::async阻塞当前线程,你需要保存返回的std::future对象,让它的生命周期超出当前语句,比如把它作为middleFunc的局部变量,或者返回给上层:
void middleFunc() { auto future = std::async(std::launch::async, threadFunc); // 保存future std::cout << "humble\n"; }
这样future的析构会延迟到middleFunc返回时,但如果你还是在main线程同步调用middleFunc,那future析构时还是会阻塞main线程。如果想完全不阻塞,你可以把future存储到一个全局容器里,或者直接用std::thread然后调用detach()。
内容的提问来源于stack exchange,提问作者cpp_mushkil

