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);
この場合、新しいスレッドが起動されるかどうかは実装に依存すると言われました。繰り返しますが、それはどういう意味ですか?
_std::async
_(_<future>
_ヘッダーの一部)関数テンプレートは、(おそらく)非同期タスクを開始するために使用されます。これは_std::future
_オブジェクトを返し、最終的に_std::async
_のパラメーター関数の戻り値を保持します。
値が必要な場合は、_std::future
_インスタンスでget()を呼び出します。これにより、将来の準備が整うまでスレッドがブロックされ、値が返されます。 _std::launch::async
_または_std::launch::deferred
_は、タスクの実行方法を指定するために、_std::async
_の最初のパラメーターとして指定できます。
std::launch::async
_は、関数呼び出しを独自の(新しい)スレッドで実行する必要があることを示します。 (ユーザー@ T.C。のコメントを考慮してください)。std::launch::deferred
_は、将来wait()
またはget()
が呼び出されるまで、関数呼び出しが延期されることを示します。これが起こる前に、未来の所有権を別のスレッドに移すことができます。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
実際にはそうではありません。追加 thread_local
保存された値と、実際にstd::async run f1 f2 f3
異なるスレッドのタスク、ただし同じstd::thread::id
私もこれに混乱し、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