いつ使用する必要がありますかstd::promise
over std::async
またはstd::packaged_task
?それぞれをいつ使用するかについての実際的な例を教えてください。
std::async
は、 std::future
を取得するためのきちんとした簡単な方法ですが、:
常に新しいスレッドを開始するとは限りません。 std::launch::async
を最初のパラメーターとして渡して強制します。
auto f = std::async( std::launch::async, func );
std::~future
デストラクタcanは、新しいスレッドが終了するまでブロックします
auto sleep = [](int s) { std::this_thread::sleep_for(std::chrono::seconds(s)); };
{
auto f = std::async( std::launch::async, sleep, 5 );
}
通常、.get()
または.wait()
ブロックのみが想定されますが、std::future
から返されたstd::async
の場合、デストラクタもブロックする可能性があるため、メインスレッドを忘れてブロックしないように注意してください。 。
std::future
がtemporary-lifeオブジェクトに格納されている場合、std::async
呼び出しはすぐにブロックされるため、auto f =
初期化を削除すると、次のブロックに10秒かかります。
auto sleep = [](int s) { std::this_thread::sleep_for(std::chrono::seconds(s)); };
{
auto f1 = std::async( std::launch::async, sleep, 5 );
auto f2 = std::async( std::launch::async, sleep, 5 );
}
std::packaged_task
それ自体はスレッドとは何の関係もありません。それは単なるファンクターであり、関連するstd::future
です。次のことを考慮してください。
auto task = [](int i) { std::this_thread::sleep_for(std::chrono::seconds(5)); return i+100; };
std::packaged_task< int(int) > package{ task };
std::future<int> f = package.get_future();
package(1);
std::cout << f.get() << "\n";
ここでは、package(1)
でタスクを実行し、タスクが戻った後、f
の準備ができているので、.get()
でブロックされません。
スレッドに非常に役立つstd::packaged_task
の機能があります。関数だけでなく、std::thread
をstd::packaged_task
で初期化することもできます。これにより、「std :: future」に到達するための非常に優れた方法が提供されます。次のことを考慮してください。
std::packaged_task< int(int) > package{ task };
std::future<int> f = package.get_future();
std::thread t { std::move(package), 5 };
std::cout << f.get() << "\n"; //block here until t finishes
t.join();
std::packaged_task
はコピーできないため、std::move
を使用して新しいスレッドに移動する必要があります。
std::promise
は強力なメカニズムです。たとえば、追加の同期を必要とせずに、新しいスレッドに値を渡すことができます。
auto task = [](std::future<int> i) {
std::cout << i.get() << std::flush;
};
std::promise<int> p;
std::thread t{ task, p.get_future() };
std::this_thread::sleep_for(std::chrono::seconds(5));
p.set_value(5);
t.join();
新しいスレッドは.get()
で私たちを待っています
したがって、一般的に、あなたの質問に答えます:
std::async
は、単純なものにのみ使用してください。いくつかの呼び出しを非ブロッキングにするためですが、上記のブロッキングに関するコメントを覚えておいてください。std::packaged_task
を使用してstd::future
に簡単にアクセスし、別のスレッドとして実行します
std::thread{ std::move(package), param }.detach();
または
std::thread t { std::move(package), param };
将来をさらに制御する必要がある場合は、std::promise
を使用してください。
std::shared_future
およびスレッド間で例外を渡す場合 std::promise::set_exception
も参照してください。