【问题标题】:Different behaviour in std::async between Visual Studio 2013 and 2015Visual Studio 2013 和 2015 之间 std::async 的不同行为
【发布时间】:2017-05-03 11:44:39
【问题描述】:

我在要异步运行函数的应用程序中使用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::async 返回的 std::future,这似乎解决了问题(应用程序没有挂起,我收到第二个消息框)。但是查看std::future 代码,我似乎看不出Visual Studio 2013 和2015 之间有什么不同。所以我的问题是std::async 的工作方式有什么改变会导致这种行为吗?

谢谢

【问题讨论】:

  • 我很确定这是 2013 年的一个错误,因为 async 返回的未来析构函数一直需要阻止。 This question 表现出相同的行为。 VS2013 对 C++11 的支持很差。 VS2015 更新 3 或 2017 更加合规。
  • 啊,async 的美妙世界,阻塞期货和魔法死锁...试试 this presentation,大约 42:30。
  • 不要尝试从单独的线程发送消息(),它很可能会阻塞。使用 PostMessage()
  • 在处理未实现的 future 的析构函数时,Microsoft 不同意 c++ 世界的其他部分。因此,c++ 的这个领域是不可移植的。这是他们抑制技术进步的阴谋的一部分。
  • 谢谢大家。非常感谢。

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


【解决方案1】:

MSVC 在 2013 年有一个错误。std::async 的返回 std::future 的析构函数必须阻塞;他们故意没有阻止它,因为他们在委员会中争论不同的标准。

他们在 MSVC 2015 之前修复了这个问题。

恢复大部分旧行为的一种方法是编写一个线程池类,并向它提交作业;只要它的所有线程都在工作并且提交了新作业,就让它产生线程。在销毁时,线程池类会清理它拥有的线程。然后使用它的作业提交机制而不是std::async

无论如何,请避免在 MSVC 上使用std::async,因为它还包含另一个违反标准(如果没有破坏)的特性,即使用受某些硬件相关常量限制的线程池;如果您有太多活动的std::async,新的将不会启动。这违反了 C++ 标准给出的建议和精神,但在技术上并不违反(因为 C++ 标准为非线程线程实现提供了荒谬的余地)。它很容易导致难以追踪只有当您的应用程序扩展并在更多地方使用std::async 时才会发生的错误。

也许这在 MSVC 的某些迭代中已修复,但我读到的最后一份报告是它仍在 2015 年。

这是编写自己的线程池作业队列的另一个原因。至少您可以控制语义,而不是获得 MSVC 当前发布的任何随机损坏或半损坏的实现。

【讨论】:

  • 谢谢。幸运的是,我没有太多活动的 std::async,我以后会尽量避免它们,因为 MSVC 实现并不好。我有一个问题。如果我有自己的函数在内部调用 std::async 并返回其结果(未来),那么考虑到返回的未来将存储在该函数调用站点的变量中,这样做是否安全?还是我必须在函数本身内设置未来变量?谢谢
  • @AliAlamiri 是的,未来的状态会发生变化。将它们视为unique_ptr<some_internal_state> 的包装。
【解决方案2】:

SendMessage 是一个阻塞调用。您应该使用 PostMessage(即发即忘)将消息发送到不同的线程。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-10-30
    • 1970-01-01
    • 2016-02-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-02
    • 1970-01-01
    相关资源
    最近更新 更多