首页 > 解决方案 > Promise 和 set_value_at_thread_exit 的生命周期

问题描述

假设我们有以下代码:

std::promise<int> promise;
auto future = promise.get_future();

const auto task = [](auto promise) {
    try {            
        promise.set_value_at_thread_exit(int_generator_that_can_throw());
    } catch (...) {
        promise.set_exception_at_thread_exit(std::current_exception());
    }
};
    
std::thread thread(task, std::move(promise));
// use future
thread.join();

我想知道这段代码是否正确和安全,如果不是,为什么。

使用 GCC 编译时它似乎工作正常,但使用 MSVC (2017) 编译时崩溃(不打印任何消息)。我的猜测是发生崩溃是因为promise内部的局部变量task超出范围并且过早销毁。如果我删除_at_thread_exit后缀,此代码将按预期工作(或似乎工作)。当 Promise 被捕获时,它也能正常工作:

const auto task = [p = std::move(promise)]() mutable {
    /*...*/
};

完整的可编译示例

标签: c++multithreadingpromise

解决方案


为什么你的代码会产生问题?让我们从“何时_at_thread_exit写入和的共享状态std::futurestd::promise”的答案开始。它发生在销毁所有线程局部变量之后。您的 lambda 在线程内被调用,并且在其范围离开后,promise 已经被销毁。但是当调用你的 lambda 的线程有一些线程局部变量时会发生什么?好吧,写入将在std::promise对象销毁后发生。实际上,其余的在标准中确实是未定义的。似乎可以在销毁后将数据传递到共享状态,std::promise但信息实际上并不存在。

最简单的解决方案当然是这样的:

std::promise<int> promise;
auto future = promise.get_future();

const auto task = [](std::promise<int>& promise) {
    try {            
        promise.set_value_at_thread_exit(int_generator_that_can_throw());
    } catch (...) {
        promise.set_exception_at_thread_exit(std::current_exception());
    }
};
    
std::thread thread(task, std::ref(promise));
// use future
thread.join();

推荐阅读