关于std::promise的set_value_at_thread_exit

当std::promise对象执行set_value_at_thread_exit()函数时,先为其关联的future设置值,然后当所在线程A(执行这个函数的线程)的所有本地对象销毁后再将其关联的future的状态设置为ready,而且其他正在等待这个异步状态的线程直到线程A退出后才会结束阻塞状态。

对于这种特性,一个重要的应用场景是:当开启一个线程后,对其执行detach()操作后,如果希望再次等待其退出,则使用这个函数来做同步。

为了测试,我们先不使用detach,而是使用jion来观察线程完全退出后它的特性。这里使用vs2015。

测试1,使用传左值引用的方式给子线程传递一个promise:

    void f(promise &p)
    {
        p.set_value_at_thread_exit(123);//可以正常使用
    }
    void main()
    {
        promise 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 &&p)
	{
		auto pp(std::move(p));
		pp.set_value_at_thread_exit(123);//pp销毁时异常退出
	}
	void main()
	{
		promise 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 &&p)
	{
		auto pp(std::move(p));
		pp.set_value(123);//可以正常使用
	}
	void main()
	{
		promise 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()函数也适用。

你可能感兴趣的:(C++11,STL,C++11,STL,多线程,promise)