web-dev-qa-db-ja.com

`std :: async`ブロッキングからフューチャーのデストラクタが返されるのはなぜですか?

別の Stackoverflow質問 に答えようとすると、この単純なC++ 11スニペットが呼び出しスレッドを暗黙的にブロックしていることに気付きました。

std::async(std::launch::async, run_async_task)

私には、これは結果を気にせずに非同期でタスクを起動する標準的なC++ 11の方法のように思えました。代わりに、これを実現するために、明らかにスレッドを明示的に作成してデタッチする必要があります(言及された質問の answer を参照)。

だからここに私の質問です:安全性/正確さに関して、std::futureのデストラクタがブロックしなければならない理由はありますか? getのみをブロックすれば十分ではないでしょうか。それ以外の場合、戻り値や例外に関心がない場合は、単に起動して忘れてしまいますか?

43
Janick Bernet

std :: asyncによって返されるフューチャーとスレッドのデストラクタをブロックする:これは物議を醸すトピックです。以下の時系列の論文リストは、委員会メンバーによる議論の一部を反映しています。

多くの議論がありましたが、C++ 14では、std :: futureおよびのデストラクタのブロッキング動作に関する変更は予定されていません。 std :: thread

あなたの質問に関して、最も興味深い論文はおそらくHans Boehmによる2番目です。私はあなたの質問に答えるためにいくつかの部分を引用します。

N3679:Async()将来のデストラクタは待機する必要があります

[..] async起動ポリシーを使用してasync()から返されたフューチャーは、関連する共有状態が準備ができるまでデストラクタで待機します。これにより、関連付けられたスレッドが引き続き実行され、関連付けられたフューチャーが破棄されたため、スレッドが完了するのを待つ手段がなくなります。それ以外の場合は完了を待機するという英雄的な努力がなければ、そのような「暴走」スレッドは、依存するオブジェクトの存続期間を超えて実行し続けることができます。

【例】

最終結果は、クロススレッドの「メモリスマッシュ」になる可能性があります。もちろん、これらの[フューチャー]が破棄される前にget()またはwait()が[..]と呼ばれる場合、この問題は回避されます。難しさ[..]は、予期しない例外によってそのコードがバイパスされる可能性があることです。したがって、通常、安全を確保するために、ある種のスコープガードが必要です。プログラマがスコープガードの追加を忘れた場合、攻撃者が生成する可能性が高いです。見落としを利用してスタックを上書きする適切な時点でのbad_alloc例外。スタックの上書きに使用されるデータを制御して、プロセスを制御することもできます。これは十分に微妙なエラーであり、私たちの経験では、実際のコードでは見落とされがちです。

更新:Michael WongのTrip Reportには、2013年9月の会議の結果に関する興味深い情報も含まれています:

C++標準会議2013年9月パート2の2からのビュー

非同期デストラクタがブロックしてはならない問題について、私たちはそれについて多くの議論をしました。 [..]かなりの支持を受けた唯一の立場は[..]非同期から返されない限り、将来のデストラクタがブロックしないという助言を与えることでした。 [..]重要な議論の後、私たちが持ち込もうとしたのはN3776だけでした。これは、非同期が存在する場合を除いて、~futureおよび~shared_futureがブロックしない位置を明確にする試みです。 Cの線に沿って非推奨を発行する試みがありました。代替なしで非同期を非推奨にします。この動きは実際にはほとんど前進していませんでした。しかし、それは手術台に到達する前でさえ死んだ。

33
nosid