std::async
| ヘッダ <future> で定義
|
||
| (1) | ||
template< class Function, class... Args> std::future<std::result_of_t<std::decay_t<Function>(std::decay_t<Args>...)>> async( Function&& f, Args&&... args ); |
(C++11以上) (C++17未満) |
|
template< class Function, class... Args> std::future<std::invoke_result_t<std::decay_t<Function>, std::decay_t<Args>...>> async( Function&& f, Args&&... args ); |
(C++17以上) (C++20未満) |
|
template< class Function, class... Args> [[nodiscard]] std::future<std::invoke_result_t<std::decay_t<Function>, std::decay_t<Args>...>> async( Function&& f, Args&&... args ); |
(C++20以上) | |
| (2) | ||
template< class Function, class... Args > std::future<std::result_of_t<std::decay_t<Function>(std::decay_t<Args>...)>> async( std::launch policy, Function&& f, Args&&... args ); |
(C++11以上) (C++17未満) |
|
template< class Function, class... Args > std::future<std::invoke_result_t<std::decay_t<Function>, std::decay_t<Args>...>> async( std::launch policy, Function&& f, Args&&... args ); |
(C++17以上) (C++20未満) |
|
template< class Function, class... Args > [[nodiscard]] std::future<std::invoke_result_t<std::decay_t<Function>, std::decay_t<Args>...>> async( std::launch policy, Function&& f, Args&&... args ); |
(C++20以上) | |
関数テンプレート async は関数 f を非同期に (場合によってはスレッドプールの一部かもしれない別のスレッドで) 実行し、その関数呼び出しの結果をいずれ保持するであろう std::future を返します。
policy に std::launch::async | std::launch::deferred を指定して (2) を呼んだかのように動作します。 別の言い方をすると、 f は別のスレッドで実行されるかもしれませんし、結果の std::future が値を問い合わされたときに同期的に実行されるかもしれません。policy に従って、引数 args で関数 f を呼びます。
- async フラグが設定されている (つまり
(policy & std::launch::async) != 0である) 場合、asyncはstd::thread(std::forward<F>(f), std::forward<Args>(args)...)によって生成されたかのように、新しい実行のスレッドで (すべてのスレッドローカル変数が初期化された状態で) callable なオブジェクトfを実行します。 その関数fが値を返したり例外を投げた場合、それはasyncの呼び出し元に返される std::future を通してアクセス可能な共有状態に格納されます。 - deferred フラグが設定されている (つまり
(policy & std::launch::deferred) != 0である) 場合、asyncはfおよびargs...を std::thread のコンストラクタと同じ方法で変換しますが、新しい実行のスレッドは生成しません。 代わりに、遅延評価が行われます。asyncが呼び出し元に返した std::future に対して時間指定のない wait 関数を最初に呼んだとき、その現在のスレッド (元々std::asyncが呼ばれたスレッドと同じである必要はありません) でargs...のコピーを (右辺値として) 渡してfのコピーが (右辺値として) 呼ばれます。 結果や例外はフューチャーに紐付く共有状態に格納され、そのときにのみ準備完了になります。 以後、同じ std::future へのアクセスはすべて、ただちに結果を返します。 policyに std::launch::async と std::launch::deferred のフラグがどちらも設定されている場合、非同期実行と遅延評価のどちらが行われるかは処理系次第です。
- async フラグが設定されている (つまり
|
(C++14以上) |
いずれの場合でも、 std::async の呼び出しは f の呼び出しに対して同期し(std::memory_order を参照してください)、 f の完了は共有状態の準備完了に対して先行配列されます。 async ポリシーが選択された場合、紐付けられたスレッドの完了は、共有状態で待機する最初の関数からの成功した戻り、または共有状態を解放する最後の関数の戻り、どちらか先に発生した方に対して同期します。
引数
| f | - | 呼び出す Callable なオブジェクト | ||||||
| args... | - | f に渡す引数
| ||||||
| policy | - | 許可する実行方法を個々のビットで制御するビットマスク値
| ||||||
| 型の要件 | ||||||||
-Function, Args は MoveConstructible の要件を満たさなければなりません。
| ||||||||
戻り値
この std::async の呼び出しによって作成された共有状態を参照する std::future。
例外
起動ポリシーが std::launch::async と等しく、処理系が新しいスレッドを開始できない場合、エラーコンディション std::errc::resource_unavailable_try_again を持つ std::system_error を投げます (ポリシーが async|deferred であるか、または追加のビットが設定されていれば、この場合、遅延評価や処理系定義のポリシーにフォールバックします)。 また、内部データ構造のためのメモリが確保できなかった場合は std::bad_alloc を投げます。
ノート
処理系は、追加の (処理系定義の) ポリシービットをデフォルトの起動ポリシーで有効化することによって、 std::async の最初のオーバーロードの動作を拡張しても構いません。
処理系定義の起動ポリシーの例としては、同期ポリシー (async 関数内でただちに実行する) やタスクポリシー (async と同様だが、スレッドローカル変数はクリアされない) などが考えられます。
std::async から取得した std::future がムーブされず、または参照に束縛され、 std::future のデストラクタが非同期操作の完了まで完全な式の終わりでブロックされる場合、実質的に以下の同期のようなコードが作成されます。
std::async(std::launch::async, []{ f(); }); // temporary's dtor waits for f()
std::async(std::launch::async, []{ g(); }); // does not start until f() completes
(std::async の呼び出し以外の方法によって取得された std::future のデストラクタはブロックすることがないことに注意してください)
例
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <future>
#include <string>
#include <mutex>
std::mutex m;
struct X {
void foo(int i, const std::string& str) {
std::lock_guard<std::mutex> lk(m);
std::cout << str << ' ' << i << '\n';
}
void bar(const std::string& str) {
std::lock_guard<std::mutex> lk(m);
std::cout << str << '\n';
}
int operator()(int i) {
std::lock_guard<std::mutex> lk(m);
std::cout << i << '\n';
return i + 10;
}
};
template <typename RandomIt>
int parallel_sum(RandomIt beg, RandomIt end)
{
auto len = end - beg;
if (len < 1000)
return std::accumulate(beg, end, 0);
RandomIt mid = beg + len/2;
auto handle = std::async(std::launch::async,
parallel_sum<RandomIt>, mid, end);
int sum = parallel_sum(beg, mid);
return sum + handle.get();
}
int main()
{
std::vector<int> v(10000, 1);
std::cout << "The sum is " << parallel_sum(v.begin(), v.end()) << '\n';
X x;
// デフォルトのポリシーを用いて (&x)->foo(42, "Hello") を呼びます。
// 並行的に「Hello 42」を表示するかもしれませんし、実行が遅延されるかもしれません。
auto a1 = std::async(&X::foo, &x, 42, "Hello");
// 遅延ポリシーを用いて x.bar("world!") を呼びます。
// a2.get() か a2.wait() が呼ばれたときに「world!」を表示します。
auto a2 = std::async(std::launch::deferred, &X::bar, x, "world!");
// 非同期ポリシーを用いて X()(43); を呼びます。
// 並行的に「43」を表示します。
auto a3 = std::async(std::launch::async, X(), 43);
a2.wait(); // prints "world!"
std::cout << a3.get() << '\n'; // prints "53"
} // この時点で a1 がまだ行われていなければ、 a1 のデストラクタがここで「Hello 42」を表示します。
出力例:
The sum is 10000
43
world!
53
Hello 42
欠陥報告
以下の動作変更欠陥報告は以前に発行された C++ 標準に遡って適用されました。
| DR | 適用先 | 発行時の動作 | 正しい動作 |
|---|---|---|---|
| LWG 2021 | C++11 | return type incorrect and value category of arguments unclear in the deferred case | corrected return type and clarified that rvalues are used |