当std::promise对象执行set_value_at_thread_exit()函数时,先为其关联的future设置值,然后当所在线程A(执行这个函数的线程)的所有本地对象销毁后再将其关联的future的状态设置为ready,而且其他正在等待这个异步状态的线程直到线程A退出后才会结束阻塞状态。
对于这种特性,一个重要的应用场景是:当开启一个线程后,对其执行detach()操作后,如果希望再次等待其退出,则使用这个函数来做同步。
为了测试,我们先不使用detach,而是使用jion来观察线程完全退出后它的特性。这里使用vs2015。
测试1,使用传左值引用的方式给子线程传递一个promise:
void f(promise<int> &p)
{
p.set_value_at_thread_exit(123);//可以正常使用
}
void main()
{
promise<int> p;
auto re = p.get_future();
thread t(f, std::ref(p));
t.join();
cout << re.get() << endl;
system("pause");
}
在主线程中定义一个promise对象,获取future并记录,然后将其以左值引用的方式传给线程函数f,接着等待线程t退出。然后获取future的值,程序正确,输出为:
123
请按任意键继续. . .
现在将函数f的参数改为传递右值引用:
void f(promise<int> &&p)
{
auto pp(std::move(p));
pp.set_value_at_thread_exit(123);//pp销毁时异常退出
}
void main()
{
promise<int> p;
auto re = p.get_future();
thread t(f, std::ref(p));
t.join();
cout << re.get() << endl;
system("pause");
}
结果在f函数退出后,销毁对象pp时异常退出,经过调试发现,promise对象析构时因为其关联的future没有被设置为ready,会去设置一个future_erro异常,此时试图锁定一个mutex时抛出了未处理的c异常,如下图所示:
经过多方查证仍未能找出其原因。
接着测试,将上面程序中的set_value_at_thread_exit改为set_value,运行正常了:
void f(promise<int> &&p)
{
auto pp(std::move(p));
pp.set_value(123);//可以正常使用
}
void main()
{
promise<int> p;
auto re = p.get_future();
thread t(f, std::ref(p));
t.join();
cout << re.get() << endl;
system("pause");
}
根据官网对set_value_at_thread_exit函数的描述得知,set_value_at_thread_exit函数会对promise对象造成某种影响,以至于在线程退出前销毁promise对象会导致异常退出。另外,如果使用set _value(),那么即使promise被销毁了,其关联的future对象仍然能使用。
如果想使用set_value_at_thread_exit函数,那么必须保证执行这个函数的线程在退出时不能销毁promise对象。这个规则对set_exception_at_thread_exit()函数,以及std::packaged_task::make_ready_at_thread_exit()函数也适用。