1. ホーム
  2. c++

[解決済み] packaged_taskとasyncの違いは何ですか?

2022-04-27 16:59:59

質問

C++11のスレッドモデルで作業しているときに、以下のことに気づきました。

std::packaged_task<int(int,int)> task([](int a, int b) { return a + b; });
auto f = task.get_future();
task(2,3);
std::cout << f.get() << '\n';

そして

auto f = std::async(std::launch::async, 
    [](int a, int b) { return a + b; }, 2, 3);
std::cout << f.get() << '\n';

は全く同じことをやっているように見えます。を実行した場合、大きな違いがあることは理解しています。 std::asyncstd::launch::deferred とありますが、この場合はあるのでしょうか?

この2つのアプローチの違い、さらに言えば、どのようなユースケースでどちらかを使うべきなのでしょうか?

どのように解決するのか?

実は、先ほどの例では、次のようなかなり長い関数を使用した場合の違いを示しています。

//! sleeps for one second and returns 1
auto sleep = [](){
    std::this_thread::sleep_for(std::chrono::seconds(1));
    return 1;
};

パッケージ化されたタスク

A packaged_task は勝手に起動しないので、呼び出す必要があります。

std::packaged_task<int()> task(sleep);

auto f = task.get_future();
task(); // invoke the function

// You have to wait until task returns. Since task calls sleep
// you will have to wait at least 1 second.
std::cout << "You can see this after 1 second\n";

// However, f.get() will be available, since task has already finished.
std::cout << f.get() << std::endl;

std::async

一方 std::asynclaunch::async は、別のスレッドでタスクを実行しようとします。

auto f = std::async(std::launch::async, sleep);
std::cout << "You can see this immediately!\n";

// However, the value of the future will be available after sleep has finished
// so f.get() can block up to 1 second.
std::cout << f.get() << "This will be shown after a second!\n";

欠点

しかし、その前に async を使用する場合、返された未来は特別な共有状態を持っており、そのために future::~future がブロックされます。

std::async(do_work1); // ~future blocks
std::async(do_work2); // ~future blocks

/* output: (assuming that do_work* log their progress)
    do_work1() started;
    do_work1() stopped;
    do_work2() started;
    do_work2() stopped;
*/

したがって、本当の非同期を望むのであれば、返された future とか、状況が変わったら結果を気にしないとか。

{
    auto pizza = std::async(get_pizza);
    /* ... */
    if(need_to_go)
        return;          // ~future will block
    else
       eat(pizza.get());
}   

この点については、Herb Sutter氏の記事を参照してください。 async~future という問題があり、Scott Meyerの std::futures から std::async 特別なことではない という、インサイトを記述しています。また、この動作に注意してください は、C++14以降で規定された しかし、C++11でも普通に実装されています。

その他の違い

を使用することで std::async では、もう特定のスレッドでタスクを実行することはできません。 std::packaged_task は他のスレッドに移動させることができます。

std::packaged_task<int(int,int)> task(...);
auto f = task.get_future();
std::thread myThread(std::move(task),2,3);

std::cout << f.get() << "\n";

また packaged_task を呼び出す前に呼び出す必要があります。 f.get() そうしないと、プログラムがフリーズしてしまい、未来が準備できないからです。

std::packaged_task<int(int,int)> task(...);
auto f = task.get_future();
std::cout << f.get() << "\n"; // oops!
task(2,3);

TL;DR

使用方法 std::async を使用することで、ある事柄をいつ完了させても構わないようにすることができます。 std::packaged_task 他のスレッドに移動したり、後で呼び出したりするために、物事をまとめたい場合。また、引用すると クリスチャン :

結局は std::packaged_task を実装するための下位機能でしかありません。 std::async (よりも多くのことができるのはそのためです)。 std::async のように、他の下位レベルのものと一緒に使用すると、より効果的です。 std::thread ). 単純に std::packaged_taskstd::function にリンクしています。 std::futurestd::async をラップして呼び出します。 std::packaged_task (おそらく別のスレッドで)。