[Windows翻译]在C++/WinRT中创建一个非敏捷的委托,第二部分:同步的coroutine。

227 阅读1分钟

原文地址:devblogs.microsoft.com/oldnewthing…

原文作者:devblogs.microsoft.com/oldnewthing…

发布时间:2020年4月7日

上一次,我们看到你可以使用ICallbackContext在另一个公寓中从你的委托人那里同步运行代码,如果调用你的委托人的代码依赖于你的返回时机,这一点很重要。

我们也可以用一个同步运行的coroutine的形式来表达。

如果我们让 await_suspend 同步调用句柄,那么这个coroutine 的延续就会和调用 co_await 的代码同步运行。

auto resume_synchronous(ICallbackContext* context)
{
  struct awaiter : std::experimental::suspend_always
  {
    ICallbackContext* context;
    bool await_suspend(
        std::experimental::coroutine_handle<> handle)
    {
      InvokeInContext(context, handle);
      return true;
    }
  };
  return awaiter{ context };
}

这简化了委托人,让你使用co_await来做脏活。

deviceWatcher.Added(
    [=, context = CaptureCurrentApartmentContext()]
    (auto&& sender, auto&& info) -> winrt::fire_and_forget
    {
        co_await resume_synchronous(context.Get());
        viewModel.Append(winrt::make<DeviceItem>(info));
    });

即使有co_await,执行也会同步进行,因为 await_suspend是同步运行连续的。

co_await是否同步恢复¹由等待者决定。如果你co_await的东西其waiter异步恢复,那么co_await将异步恢复。

deviceWatcher.Added(
    [=, context = CaptureCurrentApartmentContext()]
    (auto&& sender, auto&& info) -> winrt::fire_and_forget
    {
        auto original_context = CaptureCurrentApartmentContext();
        co_await resume_synchronous(context.Get());
        viewModel.Append(make<DeviceItem>(info));
        co_await resume_synchronous(original_context.Get());
        more_stuff();
        auto result = co_await GetMoreDataAsync();
        process_result(result);
    });

在上面的例子中,前两个co_await是同步的,但是第三个(co_await GetMoreDataAsync())大概是异步的。这意味着委托人将在第三个co_await点返回,当coroutine恢复时,引用参数(sender和info)可能不会有效。

¹或者根本就不存在。内置的 awaiter suspend_always 暂停,永远不会被唤醒。


www.deepl.com 翻译