品比三家:C++のtask based同時

3871 ワード

前の記事で述べたように、私は文章を書いて、話をするつもりですasyncpackaged_taskpromiseの違い.だから今私は穴を埋めに来ました.
TL;DR
  • async:最高レベルの抽象を提供する.スレッドの実行タイミングを制御する必要がない場合は、これを選択します.
  • packaged_task:抽象階層比async低.スレッドの実行タイミングを制御し、スレッドの実行結果であるターゲット結果を制御する必要がある場合は、これを選択します.
  • promise:抽象階層が最も低い.スレッドにターゲット結果の値を設定したい場合は、これを選択します.

  • もっと詳しく知りたいなら、下を見続けましょう.
    同局競技asyncpackaged_taskpromiseの3つに共通点があります.いずれも1つのfutureオブジェクトを返すことができ、ユーザーはこのfutureget方法で最終的な結果を得ることができます.
    次のコードでは、同じ機能をそれぞれこの3つで実現します.遅延2秒後に0を返します.
    #include 
    #include 
    #include 
    #include 
    
    int main()
    {
        std::packaged_task task([](){ 
                std::chrono::milliseconds dura( 2000  );
                std::this_thread::sleep_for( dura  );
                return 0; 
                });
        std::future f1 = task.get_future();
        std::thread(std::move(task)).detach();
    
        std::future f2 = std::async(std::launch::async, [](){ 
                std::chrono::milliseconds dura( 2000  );
                std::this_thread::sleep_for( dura  );
                return 0; 
                });
    
        std::promise p;
        std::future f3 = p.get_future();
        std::thread([](std::promise p){ 
                std::chrono::milliseconds dura( 2000  );
                std::this_thread::sleep_for( dura  );
                p.set_value(0); 
                },
                std::move(p)).detach();
    
        std::cout << "Waiting..." << std::flush;
        f1.wait();
        f2.wait();
        f3.wait();
        std::cout << "Done!
    Results are: " << f1.get() << " " << f2.get() << " " << f3.get() << "
    "; return 0; }

    上記のコードから、この3つはそれぞれ異なる抽象階層で動作していることがわかります.
  • async階層が最も高く、関数を1つ提供するだけで、futureオブジェクトが返されます.次は結果を待つだけです.
  • packaged_taskその次に、あなたはpackaged_taskを作成した後に、threadを作成し、packaged_taskそれを実行します.
  • promise最低です.threadを作成した後、対応するpromiseをパラメータとして入力します.まだ終わっていないので、関数に手動でpromiseの値を設定するのを忘れないでください.

  • では、私たちの最初の結論は明らかです.async抽象レベルが最も高いので、同時プロセスを細かく制御する必要がない限り(例えば、場合によっては)、優先的にasync非同期タスクを実行します.
    では、「場合によっては」とは何でしょうか.
    async VS. packaged_task and promise
    前述したように、async関数を受信して、futureを返します.デフォルトでは、関数はその場で実行されます.これはあなたが望んでいるものではないかもしれません.転送std::launch::deferにより、呼び出しfuture.get実行開始asyncの関数に変更できます.
    それでも、関数を実行するタイミングとfutureオブジェクトを取得するタイミングを分離したい場合は、asyncではなく、より下位のpackaged_taskpromiseを使用することが望ましい.
    BTW,async変な特性があり、async戻るfuture一時変数(またはその戻り値にかかわらず)に値を付与すると、その変数のライフサイクルが終了するまでプログラムはブロックされ、asyncの関数が完了するまでブロックされます.
    {
        std::future tmp = std::async(std::launch::async, [](){ 
                std::chrono::milliseconds dura(VERY_LONG_TIME);
                std::this_thread::sleep_for(dura);
                return 0; 
        });
        // block here for VERY_LONG_TIME
    }
    

    このような予想外の行為はC++14でキャンセルされる.だからあなたが使っているコンパイラはこの問題に遭遇しないかもしれません.
    packaged_task VS. promise
    残りの2つの中でどうやって選びますか?promiseの階層比packaged_task低いのでpromiseユーザへの制御粒度もpackaged_taskより細い.だから、もっと徹底的にコントロールしたいならpromiseを選びましょう.promiseほぼfutureの半分.対promise呼び出しset_valuefuture呼び出しset_value.packaged_taskよりもpromise関数の戻り値は気にならない――あくまでその値は手動で呼び出す必要があるset_value設定する.