[Windows翻译]在C++/WinRT中创建一个非敏捷的委托,第5部分:从一个可能已经是正确的线程中同步等待。

116 阅读1分钟

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

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

发布时间:2020年4月10日

上一次,我们研究了如何构建一个delegate,当从后台线程调用时,同步地在UI线程上完成工作,但让后台线程等待UI工作完成。我们通过将工作派遣到UI线程,并在异步活动上执行同步的get()来实现。

这是我们留下的东西。

template<typename TLambda>
void RunSyncOnDispatcher(
    CoreDispatcher const& dispatcher,
    TLambda&& lambda)
{
  [&]() -> winrt::IAsyncAction
  {
    co_await winrt::resume_foreground(dispatcher);
    lambda();
  }().get();
}

deviceWatcher.Added(
    [=](auto&& sender, auto&& info)
    {
        RunSyncOnDispatcher(Dispatcher(), [&]()
        {
            viewModel.Append(winrt::make<DeviceItem>(info));
        });
    });

不过,这里有个问题:如果你不小心从UI线程中调用这个函数怎么办?

如果你不小心从UI线程中调用了这个函数怎么办?

在这种情况下,RunSyncOnDispatcher将把工作派遣到dispatcher(也就是当前线程),然后阻塞等待工作运行。但是工作无法运行,因为它需要当前线程,而当前线程是阻塞等待工作运行的。

为了解决这个问题,我们需要检查我们是否已经在目标线程上,在这种情况下,我们只需要立即运行lambda。

template<typename TLambda>
void RunSyncOnDispatcher(
    CoreDispatcher const& dispatcher,
    TLambda&& lambda)
{
  if (dispatcher.HasThreadAccess()) {
    lambda();
  } else {
    [&]() -> winrt::IAsyncAction
    {
      co_await winrt::resume_foreground(dispatcher);
      lambda();
    }().get();
  }
}

请记住,这整整一周是专门讨论Windows Runtime事件处理的一个边缘角落案例。它本身可能并不有趣,但它确实展示了一些技术和障碍,你在编写自己的自定义例程时可能需要考虑。