std::future重赋值/移动时为何会等待已有future完成?
第一个线程未被终止的原因
当你执行f = std::async(std::launch::async, myBusyFunc)时,旧的std::future<int>对象会被销毁。对于由std::async(std::launch::async)创建的future,其析构函数有个关键行为:它会阻塞当前线程,直到关联的异步任务完全执行完毕,而不是终止或挂起线程。这就是你代码中注释// waits a minute对应的实际行为——主线程在这里会等待第一个myBusyFunc执行完成(也就是5秒的休眠加输出),之后才会创建第二个异步任务。
你观察到两个线程都正常执行,是因为第一个任务在旧future析构时被等待完成,随后第二个任务启动执行,两者是先后完成的。你的“线程变为分离状态”的推测并不准确,分离线程是指不受主线程管控、结束时自动释放资源的线程,但这里旧future的析构明确等待第一个任务结束,不存在孤儿线程的情况。
重赋值后printFutureStatus延迟输出的原因
你觉得第二个printFutureStatus输出延迟,本质是因为f = std::async(...)这一行的执行分为两步:
- 销毁旧future:主线程阻塞,直到第一个任务执行完成。
- 创建新的异步任务并赋值给f。
只有等这两步都完成后,才会执行紧接着的printFutureStatus(f)。所以这个输出并不是无法立即刷新,而是主线程先花了5秒等待第一个任务结束,才走到输出步骤。此时第二个任务刚启动,wait_for(std::chrono::seconds(0))会返回timeout,输出对应的状态。
关于段错误的推测
你之前遇到的段错误,大概率是因为主线程在std::async创建的任务完成前就提前退出了。当进程的主线程结束时,整个进程会被终止,所有正在运行的线程都会被强制终止,此时如果异步任务还在执行(比如持有未释放的资源、正在写入std::cout),就可能引发段错误或其他未定义行为。而在当前代码中,旧future的析构会等待第一个任务完成,最后f.wait()也会等待第二个任务完成,主线程会在所有任务结束后才退出,因此不会出现这类问题。
代码执行的实际流程
把你的代码执行步骤拆解一下,会更清晰:
- 主线程创建第一个异步任务,启动线程A执行
myBusyFunc(开始5秒休眠)。 - 调用
printFutureStatus(f),此时线程A未完成,输出timeout。 - 执行
f = std::async(...):- 销毁旧的future,主线程阻塞,直到线程A休眠结束、输出
done!并返回值。 - 创建第二个异步任务,启动线程B执行
myBusyFunc(开始5秒休眠)。
- 销毁旧的future,主线程阻塞,直到线程A休眠结束、输出
- 调用
printFutureStatus(f),此时线程B未完成,输出timeout。 - 调用
f.wait(),主线程阻塞直到线程B休眠结束、输出done!。 - 调用
printFutureStatus(f),此时任务已完成,输出ready。 - 主线程退出。
内容的提问来源于stack exchange,提问作者David McCabe

