web-dev-qa-db-ja.com

std :: lasync :: asyncパラメーターを使用したstd :: asyncによって起動されたスレッドに関する混乱

std::async関数について少し混乱しています。

仕様では、「あたかも新しい実行スレッドのように」実行される非同期操作(C++ 11§30.6.8/ 11)が示されています。

さて、それはどういう意味ですか?

私の理解では、コード

std::future<double> fut = std::async(std::launch::async, pow2, num);

新しいスレッドで関数pow2を起動し、変数numを値でスレッドに渡してから、将来的に関数が実行されたときに、結果をfutに配置する必要があります(長い限り関数pow2にはdouble pow2(double);のようなシグネチャがあるため)。しかし、この仕様には「あたかも」のように書かれており、それが私にとって全体をちょっと曇らせています。

質問は:

この場合、常に新しいスレッドが起動されますか?そう願っています。私にとって、パラメータstd::launch::asyncは、新しいスレッドを作成したいということを明示的に述べている意味で意味があります。

そしてコード

std::future<double> fut = std::async(std::launch::deferred, pow2, num);

pow2関数呼び出しをvar = fut.get();のようなものを書くポイントまで遅延させることにより、遅延評価を可能にする必要があります。この場合、パラメーターstd::launch::deferredは、明示的に述べていることを意味するはずです。新しいスレッドは必要ありません。戻り値が必要なときに関数が呼び出されることを確認したいだけです。

私の仮定は正しいですか?そうでない場合は、説明してください。

また、デフォルトでは、関数は次のように呼び出されることを知っています。

std::future<double> fut = std::async(std::launch::deferred | std::launch::async, pow2, num);

この場合、新しいスレッドが起動されるかどうかは実装に依存すると言われました。繰り返しますが、それはどういう意味ですか?

30
krispet krispet

_std::async_(_<future>_ヘッダーの一部)関数テンプレートは、(おそらく)非同期タスクを開始するために使用されます。これは_std::future_オブジェクトを返し、最終的に_std::async_のパラメーター関数の戻り値を保持します。

値が必要な場合は、_std::future_インスタンスでget()を呼び出します。これにより、将来の準備が整うまでスレッドがブロックされ、値が返されます。 _std::launch::async_または_std::launch::deferred_は、タスクの実行方法を指定するために、_std::async_の最初のパラメーターとして指定できます。

  1. _std::launch::async_は、関数呼び出しを独自の(新しい)スレッドで実行する必要があることを示します。 (ユーザー@ T.C。のコメントを考慮してください)。
  2. _std::launch::deferred_は、将来wait()またはget()が呼び出されるまで、関数呼び出しが延期されることを示します。これが起こる前に、未来の所有権を別のスレッドに移すことができます。
  3. _std::launch::async | std::launch::deferred_は、実装が選択できることを示します。これはデフォルトのオプションです(自分で指定しない場合)。同期的に実行することを決定できます。

この場合、常に新しいスレッドが起動されますか?

1。から、常に新しいスレッドが起動されると言えます。

私の仮定は[std :: launch :: deferredで]正しいですか?

2。から、あなたの仮定は正しいと言えます。

それはどういう意味ですか? [新しいスレッドが起動されるかどうかに関係なく、実装に依存しない]

3。から、_std::launch::async | std::launch::deferred_がデフォルトのオプションであるため、テンプレート関数_std::async_の実装が新しいスレッドを作成するかどうかを決定することを意味しますか否か。これは、一部の実装がオーバースケジューリングをチェックしている可能性があるためです。

[〜#〜] warning [〜#〜]

次のセクションはあなたの質問とは関係ありませんが、覚えておくことが重要だと思います。

C++標準では、_std::future_が非同期関数の呼び出しに対応する共有状態への最後の参照を保持している場合、そのstd :: futureのデストラクタは、非同期で実行される関数のスレッドが終了するまでブロックする必要があるとしています。したがって、_std::future_によって返される_std::async_のインスタンスは、デストラクタでブロックされます。

_void operation()
{
    auto func = [] { std::this_thread::sleep_for( std::chrono::seconds( 2 ) ); };
    std::async( std::launch::async, func );
    std::async( std::launch::async, func );
    std::future<void> f{ std::async( std::launch::async, func ) };
}
_

この誤解を招くコードにより、_std::async_呼び出しは非同期であり、実際には同期であると考えるようになります。 _std::future_によって返される_std::async_インスタンスは一時的なものであり、変数に割り当てられていないので_std::async_が戻るときにデストラクターが呼び出されるため、ブロックされます。

_std::async_の最初の呼び出しは2秒間ブロックされ、続いて_std::async_の2番目の呼び出しからさらに2秒間ブロックされます。返された_std::async_インスタンスを変数に格納するため、_std::future_の最後の呼び出しはブロックしないと考えられるかもしれませんが、それはスコープの終わりで破棄されるローカル変数なので、ローカル変数fが破棄されると、実際には関数のスコープの最後でさらに2秒間ブロックされます。

つまり、operation()関数を呼び出すと、約6秒間同期的に呼び出されるスレッドがブロックされます。このような要件は、C++標準の将来のバージョンには存在しない可能性があります。

これらのノートを編集するために使用した情報源:

C++の同時実行性:実用的なマルチスレッド、アンソニーウィリアムズ

Scott Meyersのブログ投稿: http://scottmeyers.blogspot.ca/2013/03/stdfutures-from-stdasync-arent-special.html

42
bku_drytt

実際にはそうではありません。追加 thread_local保存された値と、実際にstd::async run f1 f2 f3異なるスレッドのタスク、ただし同じstd::thread::id

0
user3833817

私もこれに混乱し、Windowsで簡単なテストを実行しました。これは、非同期の未来がOSスレッドプールスレッドで実行されることを示しています。簡単なアプリケーションでこれを実証できます。VisualStudioでのブレークアウトでは、「TppWorkerThread」という名前の実行スレッドも表示されます。

#include <future>
#include <thread>
#include <iostream>

using namespace std;

int main()
{
    cout << "main thread id " << this_thread::get_id() << endl;

    future<int> f1 = async(launch::async, [](){
        cout << "future run on thread " << this_thread::get_id() << endl;
        return 1;
    });

    f1.get(); 

    future<int> f2 = async(launch::async, [](){
        cout << "future run on thread " << this_thread::get_id() << endl;
        return 1;
    });

    f2.get();

    future<int> f3 = async(launch::async, [](){
        cout << "future run on thread " << this_thread::get_id() << endl;
        return 1;
    });

    f3.get();

    cin.ignore();

    return 0;
}

次のような出力になります。

main thread id 4164
future run on thread 4188
future run on thread 4188
future run on thread 4188
0
kaidoe