【问题标题】:co_await'ed coroutines don't run concurrently in a console applicationco_await 的协程不会在控制台应用程序中同时运行
【发布时间】:2020-01-08 23:17:13
【问题描述】:

我不明白我的小代码示例的输出。

当我在 MS C++/WinRT 代码中调试时,我看到对 WINRT_TrySubmitThreadpoolCallback() 和 WINRT_CreateThreadpoolTimer() 的调用。我认为 g() 的 3 次执行应该同时发生。

这段代码:

auto g() -> IAsyncAction {
    Log(L"Entering g()");
    co_await winrt::resume_after(1s);
    Log(L"Exiting g()");
}
auto f() -> IAsyncAction {
    Log(L"Entering f()");
    co_await winrt::resume_background();
    Log(L"First call to g()");
    co_await g();
    Log(L"Second call to g()");
    co_await g();
    Log(L"Third call to g()");
    co_await g();
    Log(L"Exiting f()");
}
auto main() -> int {
    init_apartment();
    f().get();
    return 0;
}

输出以下内容(以线程 ID 为前缀):

24000   Entering f()
13160   First call to g()
13160   Entering g()
13160   Exiting g()
13160   Second call to g()
13160   Entering g()
13160   Exiting g()
13160   Third call to g()
13160   Entering g()
13160   Exiting g()
13160   Exiting f()

它不会同时运行 g() 的实例。不应该吗?

我正在 Slack (Cpplang#coroutines) 上讨论这个问题,很多人都在努力让我理解为什么我的示例代码会这样工作。由于他们的解释不是 C++/WinRT 特定的,我希望在这里有另一个答案。

谢谢。

【问题讨论】:

  • C++/WinRT 没有发明任何魔法。这些只是您的常规 C++ 协程。如果 C++ 解释没有意义,那么任何 C++/WinRT 特定的解释也没有意义。这里没有特定于 C++/WinRT 的内容。

标签: c++-winrt c++-coroutine


【解决方案1】:

我认为 g() 的 3 次执行应该同时发生。

C++/WinRT 将 C++ 协程集成到编程模型中,以提供一种自然的方式来合作等待结果。您可以通过编写协程来生成自己的 Windows 运行时异步操作。协程是一个和其他函数一样的函数,调用者被阻塞,直到函数返回执行。您可以使用 co_await 语句协作等待返回这些异步操作类型之一的任何函数的结果。

这意味着当你调用 g() 函数时,co_await 语句将等待 g() 方法中的操作完成,然后再继续。所以行为是正确的。更多细节可以参考这个document

【讨论】:

  • 谢谢。如果是这种情况,那么调用 WINRT_TrySubmitThreadpoolCallback() 和 WINRT_CreateThreadpoolTimer() 的目的是什么?听起来你在说上面应用 co_await 与在 g() 的返回类型上调用 IAsyncAction::get() 相同。
  • 当您调用 winrt::resume_background 方法时,它会将控制权返回给调用者,然后立即在线程池线程上恢复执行。在这种情况下,您将一些工作卸载到线程池中。所以 TrySubmitThreadpoolCallback 函数使线程池在内部创建一个工作对象并立即提交其回调以执行。 CreateThreadpoolTimer() 创建一个新的定时器对象,工作线程在指定的超时时间到期后调用定时器对象的回调。
  • get() 方法会阻塞调用线程,它不适合 UI 线程。 co_await 不会。但是在您的场景中,我认为 get() 方法与 co_await 没有太大不同。
  • @fft:co_await 运算符将控制权返回给调用者。您的调用者 (main) 根本没有利用这一点,而是立即向get() 发出阻塞调用。如果您将代码更改为 auto result{ f() }; std::cout << "concurrent code\n"; result.get(); 之类的代码,您将能够观察到并发执行。
猜你喜欢
  • 2013-06-10
  • 1970-01-01
  • 2013-05-30
  • 1970-01-01
  • 1970-01-01
  • 2019-01-30
  • 2011-01-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多