原文地址: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事件处理的一个边缘角落案例。它本身可能并不有趣,但它确实展示了一些技术和障碍,你在编写自己的自定义例程时可能需要考虑。