サーチ…
前書き
プロミスと先物は、あるスレッドから別のスレッドに1つのオブジェクトをフェリーするために使用されます。
std::promise
オブジェクトは、結果を生成するスレッドによって設定されます。
std::future
オブジェクトを使用して値を取得したり、値が使用可能かどうかを調べたり、値が使用可能になるまで実行を停止したりすることができます。
std :: futureとstd :: promise
次の例では、別のスレッドによって使用される約束を設定します。
{
auto promise = std::promise<std::string>();
auto producer = std::thread([&]
{
promise.set_value("Hello World");
});
auto future = promise.get_future();
auto consumer = std::thread([&]
{
std::cout << future.get();
});
producer.join();
consumer.join();
}
遅延非同期の例
このコードはstd::async
バージョンを実装しますが、 async
がdeferred
起動ポリシーで常に呼び出されたかのように動作します。この関数には、 async
の特別なfuture
動作もありません。返還されたfuture
は価値を得ることなく破壊される可能future
があります。
template<typename F>
auto async_deferred(F&& func) -> std::future<decltype(func())>
{
using result_type = decltype(func());
auto promise = std::promise<result_type>();
auto future = promise.get_future();
std::thread(std::bind([=](std::promise<result_type>& promise)
{
try
{
promise.set_value(func());
// Note: Will not work with std::promise<void>. Needs some meta-template programming which is out of scope for this example.
}
catch(...)
{
promise.set_exception(std::current_exception());
}
}, std::move(promise))).detach();
return future;
}
std :: packaged_taskとstd :: future
std::packaged_task
は、戻り値の型に対して関数とそれに関連する約束を束ねます:
template<typename F>
auto async_deferred(F&& func) -> std::future<decltype(func())>
{
auto task = std::packaged_task<decltype(func())()>(std::forward<F>(func));
auto future = task.get_future();
std::thread(std::move(task)).detach();
return std::move(future);
}
スレッドはすぐに実行されます。私たちはそれを切り離すことも、スコープの終わりに参加させることもできます。 std :: threadの関数呼び出しが終了すると、結果は準備完了です。
これは、 std::async
とは若干異なりstd::future
返されるstd::future
は、スレッドが終了するまで実際にブロックされます。
std :: future_errorとstd :: future_errc
std :: promiseおよびstd :: futureの制約が満たされない場合、std :: future_error型の例外がスローされます。
例外内のエラーコードメンバーの型はstd :: future_errcで、値はいくつかのテストケースとともに以下のとおりです。
enum class future_errc {
broken_promise = /* the task is no longer shared */,
future_already_retrieved = /* the answer was already retrieved */,
promise_already_satisfied = /* the answer was stored already */,
no_state = /* access to a promise in non-shared state */
};
不活発な約束:
int test()
{
std::promise<int> pr;
return 0; // returns ok
}
アクティブな約束、未使用:
int test()
{
std::promise<int> pr;
auto fut = pr.get_future(); //blocks indefinitely!
return 0;
}
二重検索:
int test()
{
std::promise<int> pr;
auto fut1 = pr.get_future();
try{
auto fut2 = pr.get_future(); // second attempt to get future
return 0;
}
catch(const std::future_error& e)
{
cout << e.what() << endl; // Error: "The future has already been retrieved from the promise or packaged_task."
return -1;
}
return fut2.get();
}
std :: promise値を2回設定する:
int test()
{
std::promise<int> pr;
auto fut = pr.get_future();
try{
std::promise<int> pr2(std::move(pr));
pr2.set_value(10);
pr2.set_value(10); // second attempt to set promise throws exception
}
catch(const std::future_error& e)
{
cout << e.what() << endl; // Error: "The state of the promise has already been set."
return -1;
}
return fut.get();
}
std :: futureとstd :: async
次の単純な並列マージソートの例では、複数の並列merge_sortタスクを起動するためにstd::async
が使用されています。 std::future
は、結果を待ってそれらを同期するために使用されます:
#include <iostream>
using namespace std;
void merge(int low,int mid,int high, vector<int>&num)
{
vector<int> copy(num.size());
int h,i,j,k;
h=low;
i=low;
j=mid+1;
while((h<=mid)&&(j<=high))
{
if(num[h]<=num[j])
{
copy[i]=num[h];
h++;
}
else
{
copy[i]=num[j];
j++;
}
i++;
}
if(h>mid)
{
for(k=j;k<=high;k++)
{
copy[i]=num[k];
i++;
}
}
else
{
for(k=h;k<=mid;k++)
{
copy[i]=num[k];
i++;
}
}
for(k=low;k<=high;k++)
swap(num[k],copy[k]);
}
void merge_sort(int low,int high,vector<int>& num)
{
int mid;
if(low<high)
{
mid = low + (high-low)/2;
auto future1 = std::async(std::launch::deferred,[&]()
{
merge_sort(low,mid,num);
});
auto future2 = std::async(std::launch::deferred, [&]()
{
merge_sort(mid+1,high,num) ;
});
future1.get();
future2.get();
merge(low,mid,high,num);
}
}
注:この例では、 std::async
はポリシーstd::launch_deferred
ます。これは、すべての呼び出しで新しいスレッドが作成されないようにするためです。この例の場合、 std::async
呼び出しは順不同で行われ、 std::future::get()
呼び出しで同期します。
std::launch_async
は、すべての呼び出しで新しいスレッドを作成します。
デフォルトのポリシーはstd::launch::deferred| std::launch::async
は、実装が新しいスレッドを作成するためのポリシーを決定することを意味します。
非同期操作クラス
- std :: async:非同期操作を実行します。
- std :: future:非同期操作の結果へのアクセスを提供します。
- std :: promise:非同期操作の結果をパッケージ化します。
- std :: packaged_task:戻り値の型に対して関数とそれに関連する約束を束ねます。