web-dev-qa-db-ja.com

asyncまたはpackaged_taskでpromiseを使用するのはいつですか?

いつ使用する必要がありますかstd::promise over std::asyncまたはstd::packaged_task?それぞれをいつ使用するかについての実際的な例を教えてください。

46

std :: async

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::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::threadstd::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

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 も参照してください。

52
user2622016