std::promise左值引用与右值引用的行为差异及问题咨询
为什么std::promise右值引用传参时会抛出broken promise,而左值引用会永久阻塞?
你的观察非常准确,这个差异的核心在于std::promise的生命周期和析构行为,咱们结合代码来详细解释:
两种传参方式的本质区别
1. 右值引用传递(std::move(p))
当你用std::move(p)把promise传递给线程函数时,主线程中的p已经被转移了所有权——它变成了一个“空”的promise,不再拥有任何共享状态。而线程函数的形参std::promise<int> &&p绑定的是这个被移动后的promise实例,它是当前唯一持有该共享状态的对象。
当线程函数执行完毕时,这个形参p会被销毁。根据C++标准,如果一个std::promise在销毁时还持有未完成的共享状态(既没调用set_value/set_exception,也没通过其他方式完成状态),它会自动将共享状态设置为“异常完成”,抛出std::future_error(错误码为broken_promise)。
所以主线程调用fut.get()时,会检测到这个异常状态,直接抛出broken promise错误,而不会永久阻塞。
2. 左值引用传递(std::ref(p))
当你用std::ref(p)传递promise时,线程函数拿到的是主线程中p的引用——promise的所有权依然在主线程手里,它的生命周期由主线程控制。
当线程函数执行完毕后,主线程中的p依然存在,而且你没有调用set_value来完成共享状态。这时候关联的future会一直处于“等待”状态,主线程调用fut.get()就会永久阻塞,因为没有任何对象会去完成这个共享状态。
结合你的代码验证
咱们看你的代码片段:
#include <iostream> #include <future> #include <thread> #include <chrono> void calculateValue(std::promise<int> &&p) //void calculateValue(std::promise<int> &p) //uncomment this it will wait { using namespace std::chrono_literals; std::cout<<"This is start of thread function "<<std::endl; //Do long operations std::this_thread::sleep_for(2s); // p.set_value(8); std::cout<<"This is end of thread function "<<std::endl; } int main() { std::promise<int> p; auto fut = p.get_future(); std::thread t(calculateValue,std::move(p)); //uncomment this it will wait //std::thread t(calculateValue,std::ref(p); std::cout<<"main function ..."<<std::endl; std::cout<<"value is "<<fut.get()<<std::endl; t.join(); return 0; }
- 右值版本:线程结束时,形参
p析构,触发broken promise,fut.get()抛出异常。 - 左值引用版本:主线程的
p一直存活且未完成状态,fut.get()永久等待。
总结
- 右值传递:promise所有权转移到线程,线程结束时promise析构,自动设置异常状态。
- 左值引用传递:promise所有权在主线程,线程结束后promise仍未完成,future持续等待。
内容的提问来源于stack exchange,提问作者PapaDiHatti
相关产品推荐
相关产品推荐

