concept
std::promise
is a template class: template<typename ResultType> class promise
. Its generic parameter ResultType
is std::promise
the type of the value held by the object std::promise<int>
, which ResultType
can be of type void, for example. std::promise
Type templates provide methods to set an asynchronous result so that other threads can std::future
read the result through the instance. std::promise
And std::future
cooperation together to achieve multi-thread communication.
When a std::promise object is constructed, the object is associated with the new shared state. This shared state can be associated with an object by calling std::promise
a function. After the call , both objects share the same shared state: the std::promise object is an asynchronous provider and should set a value for the shared state at some point. The object is an asynchronous return object that can retrieve the value of the shared state and wait for it to be ready if necessary. It should be noted that it can only be called once, and multiple calls will throw an exception. In fact , the function will change the state of std::promise to ready, and when it is called again, it will find that the state is already red, and throw an exception.get_future
std::future
get_future
std::future
set_value
std::future_error
std::promise::set_xxx
std::promise
Instances can MoveConstructible
(move construction) and MoveAssignable
(move assignment), but not CopyConstructible
(copy construction) and CopyAssignable
(copy assignment).
type definition
template<typename ResultType>
class promise
{
public:
promise();
promise(promise&&) noexcept;
~promise();
promise& operator=(promise&&) noexcept;
template<typename Allocator>
promise(std::allocator_arg_t, Allocator const&);
promise(promise const&) = delete;
promise& operator=(promise const&) = delete;
void swap(promise& ) noexcept;
std::future<ResultType> get_future();
void set_value(see description);
void set_exception(std::exception_ptr p);
};
复制代码
default constructor
通过访问新的空共享状态来构造一个std::promise
对象(The object is initialized with access to a new empty shared state)。并且使用ResultType
类型的相关异步结果来构造std::promise
实例,不过异步结果并未就绪。当没有足够内存为异步结果进行分配,那么将抛出std::bad_alloc
型异常。
带分配器的构造函数
构造一个std::promise
对象,使用提供的分配器来为相关异步结果分配内存。
template<typename Allocator>
promise(std::allocator_arg_t, Allocator const& alloc);
复制代码
移动构造函数
promise(promise&& other) noexcept;
复制代码
通过另一个已存在对象,构造一个std::promise
对象。将已存在对象中的共享状态以及相关异步结果的所有权转移到新创建的std::promise
对象当中。之后,other将无关联异步结果。
#include <iostream> // std::cout
#include <thread> // std::thread
#include <future> // std::promise, std::future
#include <chrono>
std::promise<int> prom;
void print_global_promise () {
std::future<int> fut = prom.get_future();
int x = fut.get();
std::cout << "value: " << x << '\n'; // value is: 20
}
int main ()
{
std::thread th1(print_global_promise);
// 等待线程th1线跑获调用prom.get_future()
std::this_thread::sleep_for(std::chrono::seconds(1));
// 将prom所有权将转移到新创建的对象prom2上
std::promise<int> prom2(std::move(prom));
// 对prom2进行设值,在之前的prom的get也能获取到,最终输出20
prom2.set_value (20);
th1.join();
return 0;
}
复制代码
移动赋值操作符
promise& operator=(promise&& other) noexcept;
复制代码
在两个std::promise
实例中转移异步结果的所有权。在other和*this
之间进行异步结果所有权的转移。当*this
已经有关联的异步结果,那么该异步结果的状态将会为就绪态,且伴随一个std::future_error
类型异常,错误码为std::future_errc::broken_promise
。
将other中关联的异步结果转移到*this
当中后。other中将无关联异步结果。返回*this
。
#include <iostream> // std::cout
#include <thread> // std::thread
#include <future> // std::promise, std::future
std::promise<int> prom;
void print_global_promise () {
std::future<int> fut = prom.get_future();
int x = fut.get();
std::cout << "value: " << x << '\n';
}
int main () {
std::thread th1(print_global_promise);
prom.set_value(10);
th1.join();
prom = std::promise<int>(); // prom 被move赋值为一个新的 promise 对象.
std::thread th2 (print_global_promise);
prom.set_value (20);
th2.join();
return 0;
}
复制代码
swap成员函数
将两个std::promise
实例中的关联异步结果进行交换。
void swap(promise& other);
复制代码
交换other和*this
当中的关联异步结果。当swap使用other时,other中的异步结果就会与*this
中关联异步结果相交换。二者返回来亦然。
析构函数
放弃(abandon)共享状态并销毁std::promise
对象。当*this
具有关联的异步结果,并且结果中没有存储值或异常(从未调用set_xx函数),那么结果将会置为就绪,伴随一个std::future_error
异常,错误码为std::future_errc::broken_promise
。可以看下面单独章节说明例子。
get_future成员函数
返回一个与promise对象的共享状态关联的std::future对象。
std::future<ResultType> get_future();
复制代码
首先,*this
要具有关联异步结果。最后返回与*this
关联异步结果关联的std::future
实例。
当std::future
已经通过get_future()
获取过了,第二次获取将会抛出一个std::future_error
类型异常,伴随的错误码为std::future_errc::future_already_retrieved
(Only one future object can be retrieved for each promise shared state)。 调用此函数后,promise应在某个时候使其共享状态准备就绪(通过设置值或异常),否则将在销毁时自动准备就绪并包含一个std::future_error类型的异常。
#include <iostream> // std::cout
#include <future> // std::promise, std::future
int main () {
std::promise<int> prom; // 生成一个 std::promise<void> 对象.
std::future<int> fut = prom.get_future(); // 和 future 关联.
try {
std::future<int> fut2 = prom.get_future(); // 多次与 future 关联.
} catch (std::exception &e) {
std::cout << "exception: " << e.what() << '\n';
}
return 0;
}
复制代码
输出:
exception: std::future_error: Future already retrieved
复制代码
set_value成员函数
存储一个值到与*this关联的异步结果中。
void promise<void>::set_value();
void promise<R&>::set_value(R& r);
void promise<R>::set_value(R const& r);
void promise<R>::set_value(R&& r);
复制代码
首先,*this
要具有关联异步结果。最后,当ResultType
不是void型,就存储r到*this
相关的异步结果当中。*this
相关的异步结果的状态为就绪,且将值存入。任意等待异步结果的阻塞线程将解除阻塞。
如果异步结果已经存有一个值或一个异常,那么再次调用该函数将抛出std::future_error
型异常,伴随错误码为std::future_errc::promise_already_satisfied
。
#include <iostream> // std::cout
#include <functional> // std::ref
#include <thread> // std::thread
#include <future> // std::promise, std::future
void print_int(std::future<int>& fut) {
int x = fut.get(); // 获取共享状态的值.
std::cout << "value: " << x << '\n'; // 打印 value: 10.
}
int main () {
std::promise<int> prom; // 生成一个 std::promise<int> 对象.
std::future<int> fut = prom.get_future(); // 和 future 关联.
std::thread t(print_int, std::ref(fut)); // 将 future 交给另外一个线程t.
prom.set_value(10); // 设置共享状态的值, 此处和线程t保持同步.
try {
prom.set_value(20); // 多次set_value.
} catch (std::exception &e) {
std::cout << "exception: " << e.what() << '\n';
}
t.join();
return 0;
}
复制代码
输出:
value: 10
exception: std::future_error: Promise already satisfied
复制代码
set_value_at_thread_exit 成员函数
存储一个值到与*this
关联的异步结果中,标记异步结果为“已存储值”,但未就绪,直到线程退出时,异步结果的状态才会被设置为就绪 (Stores the exception pointer p in the shared state without making it ready immediately. Instead, it will be made ready automatically at thread exit, once all objects of thread storage duration have been destroyed)。
void promise<void>::set_value_at_thread_exit();
void promise<R&>::set_value_at_thread_exit(R& r);
void promise<R>::set_value_at_thread_exit(R const& r);
void promise<R>::set_value_at_thread_exit(R&& r);
复制代码
当异步结果已经存有一个值或一个异常,那么将抛出std::future_error
型异常,伴随错误码为std::future_errc::promise_already_satisfied
。
set_exception 成员函数
存储一个异常到与*this
关联的异步结果中。
void set_exception(std::exception_ptr e);
复制代码
首先,*this
具有关联异步结果。然后将e存储到*this
相关的异步结果中。在存储异常后,*this
相关的异步结果的状态将置为就绪。任何等待异步结果的阻塞线程将解除阻塞。
当异步结果已经存有一个值或一个异常,那么将抛出std::future_error
型异常,伴随错误码为std::future_errc::promise_already_satisfied
。
#include <iostream>
#include <future>
int main() {
std::promise<int> p;
std::future<int> f = p.get_future();
try {
// 可能抛出的代码
throw std::runtime_error("Example");
} catch(...) {
try {
// 存储任何抛出的异常于 promise
p.set_exception(std::current_exception());
} catch(...) {} // set_exception() 亦可能抛出
}
// 存储任何抛出的异常于 promise,自定义异常需要使用make_exception_ptr转换一下
// p.set_exception(std::make_exception_ptr(std::runtime_error("Example")));
try {
std::cout << f.get();
} catch(const std::exception& e) {
std::cout << "Exception from the thread: " << e.what() << '\n';
}
}
复制代码
输出:
Exception from the thread: Example
复制代码
set_exception_at_thread_exit 成员函数
存储一个异常到共享状态中,而不立即使状态就绪,直到当前线程退出,销毁所有拥有线程局域存储期的变量后,异步结果才被置为就绪。
void set_exception_at_thread_exit(std::exception_ptr e);
复制代码
当*this
无共享状态。伴随错误码为std::future_errc::no_state
。当异步结果已经存有一个值或一个异常,那么将抛出std::future_error
型异常,伴随错误码为std::future_errc::promise_already_satisfied
。
从未设值
如果promise对象直到析构销毁时,都没有调用set_xx接口设置过任何值,则promise会在析构时自动设置为std::future_error
错误异常,这会造成std::future.get
立刻解除阻塞并抛出std::future_error
异常。
#include <iostream> // std::cout, std::endl
#include <thread> // std::thread
#include <future> // std::promise, std::future
#include <chrono> // seconds
using namespace std::chrono;
void future_get(std::future<int> future) {
try {
future.get();
} catch(std::future_error &e) {
std::cerr << e.code() << "\n" << e.what() << std::endl;
}
}
int main() {
std::thread thread;
{
// 如果promise不设置任何值
// 则在promise析构时会自动设置为future_error
// 这会造成future.get抛出该异常
std::promise<int> promise;
thread = std::thread(future_get, promise.get_future());
}
std::cout << "promise destory here" << std::endl;
thread.join();
return 0;
}
复制代码
输出:
future:4
std::future_error: Broken promise
promise destory here
复制代码
存储自定义异常
通过std::promise::set_exception
函数可以设置自定义异常,该异常最终会被传递到std::future,并在其get函数中被抛出。
自定义异常可以通过位于头文件exception
下的std::make_exception_ptr
函数转化为std::exception_ptr
。
#include <iostream>
#include <future>
#include <thread>
#include <exception> // std::make_exception_ptr
struct MyException : public std::exception {
const char * what () const throw ()
{
return "Test promise exception";
}
};
void catch_exception(std::future<void> &future) {
try {
future.get();
} catch (MyException &e) {
std::cout << "MyException: " << e.what() << std::endl;
}
}
int main() {
std::promise<void> promise;
std::future<void> future = promise.get_future();
std::thread thread(catch_exception, std::ref(future));
// 自定义异常需要使用make_exception_ptr转换一下
promise.set_exception(std::make_exception_ptr(MyException()));
thread.join();
return 0;
}
复制代码
输出:
MyException: Test promise exception
复制代码
std::promise<void>
std::promise<void>
是合法的,它是std::promise的特例化。此时std::promise.set_value
不接受任何参数,仅用于通知关联的std::future.get()
解除阻塞,void 特化,仅用于交流无状态事件。
#include <iostream> // std::cout
#include <functional> // std::ref
#include <thread> // std::thread
#include <future> // std::promise, std::future
void print_void(std::future<void>& fut) {
fut.get(); // 获取共享状态的值void.
std::cout << "value: void" << '\n'; // 打印 value: 10.
}
int main () {
std::promise<void> prom; // 生成一个 std::promise<void> 对象.
std::future<void> fut = prom.get_future(); // 和 future 关联.
std::thread t(print_void, std::ref(fut)); // 将 future 交给另外一个线程t.
prom.set_value(); // 设置共享状态的值, 此处和线程t保持同步.
t.join();
return 0;
}
复制代码
std::promise所在线程退出时
std::promise支持定制线程退出时的行为:
std::promise::set_value_at_thread_exit
线程退出时,std::future收到通过该函数设置的值。 std::promise::set_exception_at_thread_exit
线程退出时,std::future则抛出该函数指定的异常。
#include <iostream>
#include <future>
#include <thread>
#include <chrono>
std::time_t now() {
auto t0 = std::chrono::system_clock::now();
std::time_t time_t_today = std::chrono::system_clock::to_time_t(t0);
return time_t_today; // seconds
}
int main() {
using namespace std::chrono_literals;
std::promise<int> p;
std::future<int> f = p.get_future();
std::thread([&p] {
std::this_thread::sleep_for(1s);
p.set_value_at_thread_exit(9);
}).detach();
std::cout << now() << "->Waiting...\n" << std::flush;
f.wait();
std::cout << now() << "->Done!\nResult is: " << f.get() << '\n';
}
复制代码
输出:
1647995534->Waiting...
1647995535->Done!
Result is: 9
复制代码
从上面的输出可以看到,waiting和Done之间是有间隔1s钟的,也就是说set_value_at_thread_exit
确实需要等线程结束后才会设值get为就绪解除阻塞。
std::future_errc
下面简单介绍一下std::future_errc
类型,future_errc 类枚举 : 是 future_error 类报告的所有错误提供符号名称。future_errc枚举类型的值可用于创建error_condition
对象,可以用于与future_error
异常返回的值进行比较。
名称 | 值 | 示意 |
---|---|---|
broken_promise | 0 | 与其关联的 std::promise 生命周期提前结束。 |
future_already_retrieved | 1 | 重复调用 get() 函数。 |
promise_already_satisfied | 2 | 与其关联的 std::promise 重复 set。 |
no_state | 4 | 无共享状态。 |
#include <iostream> // std::cerr
#include <future> // std::promise, std::future_error, std::future_errc
int main () {
std::promise<int> prom;
try {
prom.get_future();
prom.get_future(); // throws std::future_error with future_already_retrieved
}
catch (std::future_error& e) {
if (e.code() == std::make_error_condition(std::future_errc::future_already_retrieved))
std::cerr << "[future already retrieved]\n";
else std::cerr << "[unknown exception]\n";
}
return 0;
}
复制代码
关于std::promise
就是这些了,注意简单介绍std::promise的能力以及如何使用,如果想更深入了解该类,建议直接阅读一下源码。
参考
https//www.apiref.com/cpp-zh/cpp/…