https://yohhoy.hatenadiary.jp/entry/20120131/p1
C++11標準ライブラリで新しく追加されたstd::promise
とstd::future
についてメモ。
両者ともに標準ヘッダ <future> にて定義されるクラステンプレートであり、「別スレッドでの処理完了を待ち、その処理結果を取得する」といった非同期処理を実現するための部品*1。
int
型を扱うならstd::future<int>
, std::promise<int>
を用いる。例外はstd::exception_ptr
を利用するため任意の型を伝搬可能。)future
は計算処理の完了待ち(同期機構)と結果取り出し(通信チャネル)機能を提供する。promise
は計算処理の結果設定(通信チャネル)と完了通知(同期機構)機能を提供する。future
オブジェクトは、promise
オブジェクトのget_future
メンバ関数呼び出しにて作成する。future
, promise
オブジェクトともにコピー不可/ムーブ可能。promise
オブジェクトとそこから取り出したfuture
オブジェクトは内部的に同一の shared state を参照しており、この shared state を介して処理結果の受け渡しやスレッド間同期を実現する*3。
#include <thread>
#include <future>
void func(std::promise<double> p, double x)
{
try {
double ret = /* 何らかの計算 */;
p.set_value(ret); // (2a) promiseに戻り値を設定
} catch (...) {
p.set_exception(std::current_exception()); // (2b) promiseに例外を設定
}
}
int main()
{
std::promise<double> p;
std::future<double> f = p.get_future();
double x = 3.14159;
std::thread th(func, std::move(p), x); // (1) 別スレッドで関数funcを実行
/* 自スレッドでの処理 */;
try {
double result = f.get(); // (3a) promiseに設定された値を取得(別スレッドでの処理完了を待機)
} catch (...) {
// (3b) promiseに設定された例外が再throwされる
}
th.join(); // (4) 別スレッドの完了待ち
// future/promiseによって既に必要な同期はとられているが、thread::join()を呼ばずに
// thオブジェクトのデストラクタが呼ばれると、std::terminate()が呼び出されてしまう。
return 0;
}
future
/promise
クラステンプレートでは、2つのテンプレート特殊化(lvalue reference型による部分特殊化, void
型による特殊化)が提供される。promise::set_value()
とfuture::get()
の引数/戻り値が異なる。
// R(プライマリテンプレート)
void promise::set_value(const R& r);
void promise::set_value(R&& r);
R future::get();
// R&
void promise<R&>::set_value(R& r);
R& future<R&>::get();
// void
void promise<void>::set_value();
void future<void>::get();
future
/promise
に対する操作でエラーが生じた場合、例外std::future_error
が送出される。このfuture_error
オブジェクトにはエラーコード(std::future_errc
列挙型)が格納されており、例外の発生原因を確認できる。
標準ヘッダ <future> では、future関連のエラーコードとして下記4つを定義している。
broken_promise
future_already_retrieved