我在应用程序中使用std::async
,我希望以异步方式运行函数,并从该函数使用SendMessage
函数与工作线程的UI进行通信。以下是MFC测试应用程序的摘录,演示了我正在做的事情:
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);
}
在Visual Studio 2013中,上面的代码按预期工作。当我按下按钮时,我会看到一个显示“Hello World”的消息框,一旦我在该消息框上单击“确定”,我会收到另一个显示“Hello World Again”的消息框。
我面临的问题是,一旦我将上述代码迁移到Visual Studio 2015编译器,应用程序就会在SendMessage
函数调用后挂起。
在线阅读(回答this question)它提到了std::future
阻止的析构函数。我已经更改了Visual Studio 2015中的代码来存储来自std::future
的返回的std::async
,这似乎解决了问题(应用程序没有挂起,我得到第二个消息框)。但是看看std::future
代码,我似乎看不出Visual Studio 2013和2015之间有什么不同。所以我的问题是std::async
的工作方式有什么变化会导致这种行为?
由于
答案 0 :(得分:4)
MSVC在2013年遇到了一个错误。std::async
返回的std::future
的析构函数必须阻止;他们故意没有阻止它,因为他们在委员会中争论不同的标准。
他们在MSVC 2015之前解决了这个问题。
回退大多数旧行为的方法是编写一个线程池类,并向其提交作业;让它在所有线程都工作并提交新作业时生成线程。在销毁时,线程池类清除它拥有的线程。然后使用它的作业提交机制而不是std::async
。
在任何情况下,请避免在MSVC上使用std::async
,因为它还包含另一个使用受某些硬件相关常量限制的线程池的标准(如果没有中断)功能;如果你有太多活跃的std::async
,新的将无法启动。这违反了C ++标准给出的建议和精神,但在技术上并没有违反(因为C ++标准给非线程实现提供了荒谬的余地)。它很容易导致难以追踪只有当您的应用程序扩展并在更多位置使用std::async
时才会发生的错误。
也许这在MSVC的某些迭代中已得到修复,但我上次读到的报告是它仍然在2015年。
这是编写自己的线程池作业队列的另一个原因。至少你可以控制语义,而不是获得MSVC当前发布的任何随机破坏或半破坏的实现。
答案 1 :(得分:1)
SendMessage是一个阻止调用。你应该使用PostMessage(fire& forget)来发送消息到另一个线程。