Visual Studio 2013和2015之间std :: async中的不同行为

时间:2017-05-03 11:44:39

标签: c++ c++11 winapi visual-c++

我在应用程序中使用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的工作方式有什么变化会导致这种行为?

由于

2 个答案:

答案 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)来发送消息到另一个线程。