- C++ 多线程09:std::promise
- 概念
- 默认构造函数
- 带分配器的构造函数
- 移动构造函数
- 移动赋值操作符
- swap成员函数
- 析构函数
- get_future成员函数
- set_value成员函数
- set_value_at_thread_exit 成员函数
- set_exception 成员函数
- set_exception_at_thread_exit 成员函数
- 从未设值
- 存储自定义异常
- `std::promise
` - std::promise所在线程退出时
- std::future_errc
std::promise
是一个模板类:template
。其泛型参数ResultType
是std::promise
对象保存的值的类型,例如std::promise
,ResultType
可以是void类型。std::promise
类型模板提供设置异步结果的方法,这样其他线程就可以通过std::future
实例来读取该结果。std::promise
和std::future
合作共同实现了多线程间通信。
在构造std::promise对象时,该对象与新的共享状态(shared state)关联。通过调用std::promise
的get_future
函数,可以将该共享状态与std::future
对象关联。调用get_future
之后,两个对象共享相同的共享状态:std::promise对象是异步提供程序(asynchronous provider),应在某个时刻为共享状态设置一个值。std::future
对象是个异步返回对象,可以检索共享状态的值,并在必要时等待其准备就绪。 需要注意的是:set_value
只能被调用一次,多次调用会抛出std::future_error
异常。事实上std::promise::set_xxx
函数会改变std::promise的状态为ready,再次调用时发现状态已要是reday了,则抛出异常。
std::promise
实例是可以MoveConstructible
(移动构造)和MoveAssignable
(移动赋值),但是不能CopyConstructible
(拷贝构造)和CopyAssignable
(拷贝赋值)。
类型定义
templateclass promise
{public:
promise();
promise(promise&&) noexcept;
~promise();
promise& operator=(promise&&) noexcept;
templatepromise(std::allocator_arg_t, Allocator const&);
promise(promise const&) = delete;
promise& operator=(promise const&) = delete;
void swap(promise& ) noexcept;
std::futureget_future();
void set_value(see description);
void set_exception(std::exception_ptr p);
};
复制代码
默认构造函数通过访问新的空共享状态来构造一个std::promise
对象(The object is initialized with access to a new empty shared state)。并且使用ResultType
类型的相关异步结果来构造std::promise
实例,不过异步结果并未就绪。当没有足够内存为异步结果进行分配,那么将抛出std::bad_alloc
型异常。
构造一个std::promise
对象,使用提供的分配器来为相关异步结果分配内存。
templatepromise(std::allocator_arg_t, Allocator const& alloc);
复制代码
移动构造函数promise(promise&& other) noexcept;
复制代码
通过另一个已存在对象,构造一个std::promise
对象。将已存在对象中的共享状态以及相关异步结果的所有权转移到新创建的std::promise
对象当中。之后,other将无关联异步结果。
#include // std::cout
#include // std::thread
#include // std::promise, std::future
#includestd::promiseprom;
void print_global_promise () {std::futurefut = 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::promiseprom2(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 // std::cout
#include // std::thread
#include // std::promise, std::future
std::promiseprom;
void print_global_promise () {std::futurefut = 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(); // 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
。可以看下面单独章节说明例子。
返回一个与promise对象的共享状态关联的std::future对象。
std::futureget_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 // std::cout
#include // std::promise, std::future
int main () {std::promiseprom; // 生成一个 std::promise对象.
std::futurefut = prom.get_future(); // 和 future 关联.
try {std::futurefut2 = 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::set_value();
void promise::set_value(R& r);
void promise::set_value(R const& r);
void promise::set_value(R&& r);
复制代码
首先,*this
要具有关联异步结果。最后,当ResultType
不是void型,就存储r到*this
相关的异步结果当中。*this
相关的异步结果的状态为就绪,且将值存入。任意等待异步结果的阻塞线程将解除阻塞。
如果异步结果已经存有一个值或一个异常,那么再次调用该函数将抛出std::future_error
型异常,伴随错误码为std::future_errc::promise_already_satisfied
。
#include // std::cout
#include // std::ref
#include // std::thread
#include // std::promise, std::future
void print_int(std::future& fut) {int x = fut.get(); // 获取共享状态的值.
std::cout<< "value: "<< x<< '\n'; // 打印 value: 10.
}
int main () {std::promiseprom; // 生成一个 std::promise对象.
std::futurefut = 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::set_value_at_thread_exit();
void promise::set_value_at_thread_exit(R& r);
void promise::set_value_at_thread_exit(R const& r);
void promise::set_value_at_thread_exit(R&& r);
复制代码
当异步结果已经存有一个值或一个异常,那么将抛出std::future_error
型异常,伴随错误码为std::future_errc::promise_already_satisfied
。
存储一个异常到与*this
关联的异步结果中。
void set_exception(std::exception_ptr e);
复制代码
首先,*this
具有关联异步结果。然后将e存储到*this
相关的异步结果中。在存储异常后,*this
相关的异步结果的状态将置为就绪。任何等待异步结果的阻塞线程将解除阻塞。
当异步结果已经存有一个值或一个异常,那么将抛出std::future_error
型异常,伴随错误码为std::future_errc::promise_already_satisfied
。
#include#includeint main() {std::promisep;
std::futuref = 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// std::cout, std::endl
#include// std::thread
#include// std::promise, std::future
#include// seconds
using namespace std::chrono;
void future_get(std::futurefuture) {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::promisepromise;
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#include#include#include// std::make_exception_ptr
struct MyException : public std::exception {const char * what () const throw ()
{return "Test promise exception";
}
};
void catch_exception(std::future&future) {try {future.get();
} catch (MyException &e) {std::cout<< "MyException: "<< e.what()<< std::endl;
}
}
int main() {std::promisepromise;
std::futurefuture = 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
std::promise
是合法的,它是std::promise的特例化。此时std::promise.set_value
不接受任何参数,仅用于通知关联的std::future.get()
解除阻塞,void 特化,仅用于交流无状态事件。
#include // std::cout
#include // std::ref
#include // std::thread
#include // std::promise, std::future
void print_void(std::future& fut) {fut.get(); // 获取共享状态的值void.
std::cout<< "value: void"<< '\n'; // 打印 value: 10.
}
int main () {std::promiseprom; // 生成一个 std::promise对象.
std::futurefut = 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#include#include#includestd::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::promisep;
std::futuref = 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
类型,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 // std::cerr
#include // std::promise, std::future_error, std::future_errc
int main () {std::promiseprom;
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/…
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧
本文标题:C++多线程:std::promise-创新互联
转载来源:http://scgulin.cn/article/ddghio.html