C++11同時ガイド4(<br>詳細2 std::packaged_task紹介)

18968 ワード

前回「C++11同時ガイド4(<future>詳しくはstd::promise紹介)」では主にヘッダファイルのstd::promiseクラスについて説明しましたが、本文ではstd::packaged_について説明します.task.
std::packaged_taskは呼び出し可能なオブジェクトをパッケージ化し、呼び出し可能なオブジェクトをパッケージ化する意味でstd::packaged_taskはstd::functionと似ていますが、std::packaged_taskは、パッケージされた呼び出し可能なオブジェクトの実行結果をstd::futureオブジェクトに渡します(このオブジェクトは、通常、std::packaged_taskタスクの実行結果を別のスレッドで取得します).
std::packaged_taskオブジェクトの内部には、2つの最も基本的な要素が含まれています.1つは、パッケージされたタスク(stored task)、タスク(task)は、関数ポインタ、メンバー関数ポインタ、または関数オブジェクトなどの呼び出し可能なオブジェクトです.2つは、タスクの戻り値を保存するために使用され、std::futureオブジェクトを使用して非同期で共有状態にアクセスできる共有状態の効果です.
std::packged_task::get_futureは、共有状態に関連付けられたstd::futureオブジェクトを取得します.この関数を呼び出すと、2つのオブジェクトは同じ共有状態を共有します.具体的には、次のように説明します.
  • std::packaged_taskオブジェクトは非同期Providerであり、ある時点でパッケージされたタスクを呼び出すことで共有状態の値を設定します.
  • std::futureオブジェクトは非同期の戻りオブジェクトであり、それによって共有状態の値を得ることができ、もちろん必要に応じて共有状態フラグがreadyになるのを待つ必要がある.

  • std::packaged_taskの共有状態のライフサイクルは、最後に関連付けられたオブジェクトが解放または破棄されるまで続く.次の小さな例ではstdについて概説します::packaged_taskの使い方:
    #include <iostream>     // std::cout
    #include <future>       // std::packaged_task, std::future
    #include <chrono>       // std::chrono::seconds
    #include <thread>       // std::thread, std::this_thread::sleep_for
    
    // count down taking a second for each value:
    int countdown (int from, int to) {
        for (int i=from; i!=to; --i) {
            std::cout << i << '
    '; std::this_thread::sleep_for(std::chrono::seconds(1)); } std::cout << "Finished!
    "; return from - to; } int main () { std::packaged_task<int(int,int)> task(countdown); // packaged_task std::future<int> ret = task.get_future(); // packaged_task future . std::thread th(std::move(task), 10, 0); // . int value = ret.get(); // . std::cout << "The countdown lasted for " << value << " seconds.
    "; th.join(); return 0; }

    実行結果:
    concurrency ) ./Packaged_Task1 
    10
    9
    8
    7
    6
    5
    4
    3
    2
    1
    Finished!
    The countdown lasted for 10 seconds.
    

    std::packaged_taskコンストラクタ
    default (1)
    packaged_task() noexcept;
    

    initialization (2)
    template <class Fn>
      explicit packaged_task (Fn&& fn);
    

    with allocator (3)
    template <class Fn, class Alloc>
      explicit packaged_task (allocator_arg_t aa, const Alloc& alloc, Fn&& fn);
    

    copy [deleted] (4)
    packaged_task (const packaged_task&) = delete;
    

    move (5)
    packaged_task (packaged_task&& x) noexcept;

    std::packaged_taskコンストラクション関数は5つの形式がありますが、コピーコンストラクションは無効になっています.以下では、上記の構造関数の意味を簡単に説明します.
  • デフォルトのコンストラクション関数は、空の共有状態を初期化し、packaged_taskオブジェクトにはパッケージタスクがありません.
  • は共有状態を初期化し、パッケージタスクはパラメータfnによって指定される.
  • カスタムメモリディスペンサ付きコンストラクタは、デフォルトのコンストラクタと同様ですが、カスタムディスペンサを使用して共有ステータスを割り当てます.
  • はコンストラクション関数をコピーし、無効にします.
  • 構造関数を移動します.

  • 次の例では、構造関数の使用方法を説明します.
    #include <iostream>     // std::cout
    #include <utility>      // std::move
    #include <future>       // std::packaged_task, std::future
    #include <thread>       // std::thread
    
    int main ()
    {
        std::packaged_task<int(int)> foo; //  .
    
        //   lambda   packaged_task  .
        std::packaged_task<int(int)> bar([](int x){return x*2;});
    
        foo = std::move(bar); // move- ,  C++11  .
    
        //   packaged_task   future  .
        std::future<int> ret = foo.get_future();
    
        std::thread(std::move(foo), 10).detach(); //  , .
    
        int value = ret.get(); //  .
        std::cout << "The double of 10 is " << value << ".
    "; return 0; }

    std::promiseと同様、std::packaged_taskも通常の付与操作演算を無効にし、move付与演算のみを許可した.
    std::packaged_task::valid紹介
    現在のpackaged_を確認taskが有効な共有状態に関連付けられているかどうか、デフォルトのコンストラクション関数によって生成されたpackaged_taskオブジェクト.この関数はfalseを返します.move付与操作またはswap操作が途中で行われない限り.
    次の例を参照してください.
    #include <iostream>     // std::cout
    #include <utility>      // std::move
    #include <future>       // std::packaged_task, std::future
    #include <thread>       // std::thread
    
    //   int(int) packaged_task.
    std::future<int> launcher(std::packaged_task<int(int)>& tsk, int arg)
    {
        if (tsk.valid()) {
            std::future<int> ret = tsk.get_future();
            std::thread (std::move(tsk),arg).detach();
            return ret;
        }
        else return std::future<int>();
    }
    
    int main ()
    {
        std::packaged_task<int(int)> tsk([](int x){return x*2;});
    
        std::future<int> fut = launcher(tsk,25);
    
        std::cout << "The double of 25 is " << fut.get() << ".
    "; return 0; }

    std::packaged_task::get_future紹介
    packagedと戻るtaskオブジェクト共有状態に関連するfutureオブジェクト.返されるfutureオブジェクトは、packaged_に別のスレッドで取得できます.taskオブジェクトの共有状態に設定された値または異常.
    例を見てください(実は前にget_futureの例を説明しました):
    #include <iostream>     // std::cout
    #include <utility>      // std::move
    #include <future>       // std::packaged_task, std::future
    #include <thread>       // std::thread
    
    int main ()
    {
        std::packaged_task<int(int)> tsk([](int x) { return x * 3; })); // package task
    
        std::future<int> fut = tsk.get_future();   //   future  .
    
        std::thread(std::move(tsk), 100).detach();   //  packaged_task.
    
        int value = fut.get();                     //  ,  .
    
        std::cout << "The triple of 100 is " << value << ".
    "; return 0; }

    std::packaged_task::operator()(Args...args)紹介
    パッケージを呼び出す_taskオブジェクトが包装するオブジェクト(通常は関数ポインタ、関数オブジェクト、lambda式など)であり、入力パラメータはargsである.この関数を呼び出すと、一般的に2つの状況が発生します.
  • packagedが正常に呼び出された場合taskでラッピングされたオブジェクトは、ラッピングされたオブジェクトに戻り値があればpackaged_に保存されます.taskの共有状態にあります.
  • packagedが呼び出された場合taskでパッケージされたオブジェクトが失敗し、例外が投げ出されるとpackaged_にも例外が保存されます.taskの共有状態にあります.

  • いずれの場合も共有状態のフラグがreadyとなるため、他の共有状態を待つスレッドは共有状態の値や異常を取得して実行を継続することができる.
    共有状態の値は、futureオブジェクト(get_futureで取得)でgetを上げることによって得ることができる.
    パッケージされたタスクはpackaged_task構築時に指定するのでoperator()を呼び出す効果はpackaged_taskオブジェクトの構築時に指定された呼び出し可能なオブジェクトによって決定されます.
  • パッケージされたタスクが関数ポインタまたは関数オブジェクトである場合、std::packaged_を呼び出すtask::operator()は、パッケージされたオブジェクトにパラメータを渡すだけです.
  • パッケージされたタスクがクラスの非静的メンバー関数へのポインタである場合std::packaged_task::operator()の最初のパラメータは、メンバー関数が呼び出されたオブジェクトとして指定され、残りのパラメータはメンバー関数のパラメータとして指定されます.
  • パッケージされたタスクがクラスの非静的メンバー変数を指す場合std::packaged_task::operator()は単一のパラメータのみを許可します.

  • std::packaged_task::make_ready_at_thread_exit紹介
    この関数は、パッケージされたタスクを呼び出し、std::packaged_のようなパラメータをタスクに渡します.taskのoperator()メンバー関数.しかしoperator()関数とは異なりmake_ready_at_thread_exitは直ちに共有状態のフラグをreadyと設定するのではなく,スレッド終了時に共有状態のフラグを設定する.
    packaged_とtask共有状態に関連付けられたfutureオブジェクトがfuture::getで待機すると、現在のfuture::get呼び出しはスレッドが終了するまでブロックされます.スレッドが終了するとfuture::get呼び出しが実行を続行したり、例外を投げ出したりします.なお、この関数はpromise共有状態の値を設定しており、スレッドが終了する前に他の設定や共有状態の値を変更する操作がある場合、future_error( promise_already_satisfied ).
    std::packaged_task::reset()紹介
    パッケージをリセット_taskの共有状態ですが、以前のパッケージされたタスクは保持されます.例を見てください.この例ではpackaged_taskは複数回再利用されました.
    #include <iostream>     // std::cout
    #include <utility>      // std::move
    #include <future>       // std::packaged_task, std::future
    #include <thread>       // std::thread
    
    // a simple task:
    int triple (int x) { return x*3; }
    
    int main ()
    {
        std::packaged_task<int(int)> tsk (triple); // package task
    
    
        std::future<int> fut = tsk.get_future();
        std::thread (std::move(tsk), 100).detach();
        std::cout << "The triple of 100 is " << fut.get() << ".
    "; // re-use same task object: tsk.reset(); fut = tsk.get_future(); std::thread(std::move(tsk), 200).detach(); std::cout << "Thre triple of 200 is " << fut.get() << ".
    "; return 0; }

    std::packaged_task::swap()紹介
    交換パッケージ_taskの共有状態.
    はい、std::packaged_taskはここまで紹介し、http://www.cplusplus.com/reference/future/packaged_task/に関する内容を参照した.次の記事ではstd::future,std::shared_をご紹介しますfutureおよびstd::future_error、またヘッダファイルのstd::async,std::future_についても紹介しますcategory関数および関連列挙タイプ.