2017-05-03 13 views
0

私は関数を非同期で実行したいアプリケーションでstd::asyncを使用しています。この関数からはSendMessage関数を使用してワーカースレッドからUIに話します。 Visual Studioの2013年にVisual Studio 2013と2015の間でstd :: asyncの動作が異なります。

LRESULT CStdAsyncProjDlg::OnUserPlusOne(WPARAM, LPARAM) 
{ 
    MessageBox("Message", "Hello World Again", MB_OK); 

    return 0; 
} 

// Function that contains the async work. 
void TestAsync(HWND hDlg) 
{ 
    // Send a message to the UI from this worker. The WM_USER + 1 message 
    // handler is CStdAsyncProjDlg::OnUserPlusOne 
    SendMessage(hDlg, WM_USER + 1, 0, 0); 
} 

// This event is fired when a button is pressed on the MFC dialog. 
void CStdAsyncProjDlg::OnBnClickedButton1() 
{ 
    MessageBox("Message", "Hello World", MB_OK); 
    std::async(std::launch::async, TestAsync, m_hWnd); 
} 

を期待通りに上記のコードは動作します。以下は、私がやっている実証MFCのテストアプリケーションからの抽出物です。ボタンを押すと "Hello World"というメッセージボックスが表示され、そのメッセージボックスで[OK]をクリックすると "Hello World Again"というメッセージボックスが表示されます。

私が直面している問題は、上記のコードをVisual Studio 2015コンパイラに移行すると、SendMessage関数呼び出し後にアプリケーションがハングすることです。

オンラインで(this questionへの回答)読むと、std::futureのデストラクタが記述されています。私はから返されたstd::futureを格納するためにVisual Studio 2015のコードを変更しました。それは問題を修正するようです(アプリケーションがハングしないため、2番目のメッセージボックスが表示されます)。しかし、std::futureのコードを見ると、Visual Studio 2013と2015の間に何か違うものは見えないようです。そのため、私の質問には何か変わったのですが、そのような動作の原因となるのはstd::asyncですか?

おかげ

+1

「async」によって返された未来のデストラクタが常にブロックされる必要があったので、2013年にはバグだったと確信しています。 [この質問](http://stackoverflow.com/questions/43600159/msvc-2013-stdasync-works-asynchronously-without-wait)は同じ動作を示します。 VS2013には、C++ 11のサポートがうまくいきませんでした。 VS2015のアップデート3または2017はより忠実です。 – NathanOliver

+0

ああ、未来のことと魔法のデッドロックを阻む「非同期」の素晴らしい世界... 42:30頃、[このプレゼンテーション](https://www.youtube.com/watch?v=QIHy8pXbneI)を試してみてください。 –

+1

別のスレッドからSendMessage()を試行しないでください。おそらくブロックされます。 Use PostMessage() –

答えて

4

はMSVCはデストラクタブロックしなければならないのは、std::futureが返される "2013年std::asyncにバグがありました。彼らは故意にそれを阻止しなかった。なぜなら、彼らは委員会で異なる基準を主張するからだ。

彼らは、以前の動作のほとんどを取り戻すための方法は、スレッドプールのクラスを記述し、それにジョブを送信することであるMSVC 2015年前に

これを固定し、すべてのスレッドが動作し、新しいジョブがサブミットされるたびにスレッドを生成させます。破壊時には、スレッドプールクラスは自身が所有するスレッドをクリーンアップします。次に、std::asyncの代わりにジョブ送信メカニズムを使用します。

いずれにしても、ハードウェアに依存する定数によって制限されたスレッドプールを使用するという点で、標準に反しない(破損していない場合でも)機能が含まれているため、std::asyncをMSVCで使用しないでください。アクティブな番号があまりにも多い場合は、新しいものが起動しません。std::asyncこれはC++標準のアドバイスと精神に違反していますが、技術的に違反しているわけではありません(C++標準では、スレッド化されていないスレッド実装にばかげた余裕が与えられています)。アプリケーションのスケールアップ時にのみ発生するバグを追跡するのが難しくなり、さらに多くの場所でstd::asyncが使用されます。

はたぶんこれはMSVCのいくつかの反復で修正されましたが、私は読んで最後のレポートは、これはあなた自身のスレッドプールのジョブキューを書くために別の理由で2015年

にまだあったということでした。 MSVCが現在出荷されているランダムな壊れた実装や半分壊れた実装を取得する代わりに、少なくともセマンティクスを制御できます。

+0

ありがとう。幸いにも、私はあまりにも多くのアクティブなstd :: asyncを持っていませんし、MSVCの実装が素晴らしいとは言えないので、将来私はそれらを避けようとします。私は1つ質問してください。内部的にstd :: asyncを呼び出し、その結果(未来)を返す独自の関数を持っていれば、返された未来はその関数の呼び出しサイトから変数に格納されるので安全ですか?あるいは、関数自体の内部に将来の変数を設定する必要がありますか?ありがとう –

+0

@AliAlamiriはい、将来の状態は動きます。それらを 'unique_ptr 'の周りのラッパーと考えてください。 – Yakk

関連する問題