7

futureから戻った理由には、将来のデストラクタで発生したstd::async特別な共有状態があることを知りました。wait on returned futureしかし、 を使用するstd::pakaged_taskと、その future は同じ動作を示しません。パッケージ化されたタスクを完了するには、 からオブジェクトを明示的に呼び出す必要がget()ありfutureますpackaged_task

今私の質問は次のとおりです。

  1. 将来の内部実装は何ですか (思考std::asyncvs std::packaged_task)?
  2. futurereturn fromに同じ動作が適用されなかったのはなぜstd::packaged_taskですか? または、言い換えれば、同じ動作がどのように停止されるのstd::packaged_task futureでしょうか?

コンテキストを確認するには、次のコードを参照してください。

countdownタスクが完了するのを待ちません。ただし、コメントを外すと、返された未来を文字通りブロックしているため、// int value = ret.get();終了し、明らかです。countdown

    // packaged_task example
#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::endl;
    std::this_thread::sleep_for(std::chrono::seconds(1));
  }
  std::cout << "Lift off!" <<std::endl;
  return from-to;
}

int main ()
{
   std::cout << "Start " << std::endl;
  std::packaged_task<int(int,int)> tsk (countdown);   // set up packaged_task
  std::future<int> ret = tsk.get_future();            // get future

  std::thread th (std::move(tsk),10,0);   // spawn thread to count down from 10 to 0

//   int value = ret.get();                  // wait for the task to finish and get result

  std::cout << "The countdown lasted for " << std::endl;//<< value << " seconds.\n";

  th.detach();   

  return 0;
}

返されたオブジェクトで使用するかどうかに関係なく、別のスレッドでstd::asyncタスクを実行するために使用すると、常にタスクが終了します。countdownget()future

// packaged_task example
#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::endl;
        std::this_thread::sleep_for(std::chrono::seconds(1));
      }
      std::cout << "Lift off!" <<std::endl;
      return from-to;
    }
    
    int main ()
    {
       std::cout << "Start " << std::endl;
      std::packaged_task<int(int,int)> tsk (countdown);   // set up packaged_task
      std::future<int> ret = tsk.get_future();            // get future
    
      auto fut = std::async(std::move(tsk), 10, 0);   

    
    //   int value = fut.get();                  // wait for the task to finish and get result
    
      std::cout << "The countdown lasted for " << std::endl;//<< value << " seconds.\n";

      return 0;
    }
4

3 に答える 3

5

std::async与えられたタスクがどこでどのように実行されるかについて明確な知識を持っています。それがその仕事です:タスクを実行することです。そのためには、実際にどこかに配置する必要があります。その場所は、スレッド プール、新しく作成されたスレッド、またはfuture.

asyncは関数がどのように実行されるかを知っているため、潜在的な非同期実行が終了したときに通信できるメカニズムfutureを構築するために必要な情報を 100% 持っています。その関数を実行すると、最終的に実際に実行されます。結局のところ、それはそのメカニズムが何であるかを知っています。

しかし、packaged_task そうではありません。すべてのpackaged_task機能は、指定された引数で呼び出すことができる呼び出し可能なオブジェクトを格納しpromise、関数の戻り値の型で を作成futureし、値を生成する関数を取得して実行する手段を提供することです。

タスクが実際にいつどこで実行されるかは、 の仕事ではありませんpackaged_task。その知識がなければ、futureのデストラクタをタスクと同期させるために必要な同期を構築することはできません。

新しく作成されたスレッドでタスクを実行したいとしましょう。その実行をfutureの破棄と同期させるには、タスク スレッドが終了するまでデストラクタがブロックするミューテックスが必要です。

futureしかし、のデストラクタの呼び出し元と同じスレッドでタスクを実行したい場合はどうなるでしょうか? それなら、すべて同じスレッド上にあるため、ミューテックスを使用して同期することはできません。代わりに、デストラクタにタスクを呼び出させる必要があります。これはまったく異なるメカニズムであり、実行方法に左右されます。

packaged_taskどのように実行するつもりなのかわからないため、それを行うことはできません。

これは に固有のものではないことに注意してくださいpackaged_task。ユーザーが作成したオブジェクトから作成されたすべて の は、 のの特別なプロパティを持ちません。futurepromiseasyncfuture

したがって、問題は、なぜ他の人がそうしないasyncのかではなく、なぜこのように機能するのかということです。

それを知りたいのであれば、それは 2 つの競合するニーズによるものです。async非同期実行を取得するための高レベルで脳死状態の単純な方法が必要であり (そのためには、破棄時の同期が理にかなっています)、誰も新しいものを作成したくありませんでした。futureデストラクタの動作を除いて、既存のものと同一の型。futureそのため、実装と使用を複雑にして、仕組みをオーバーロードすることにしました。

于 2020-10-15T01:43:30.363 に答える