原文地址:devblogs.microsoft.com/oldnewthing…
原文作者:devblogs.microsoft.com/oldnewthing…
发布时间:2020年4月6日
在C++/WinRT中,默认情况下,代表是敏捷的,这意味着它们可以在任何线程上运行。这意味着它们可以在任何线程上运行。这通常是一件好事。
然而,有一个奇怪的角落情况,你可能需要一个非敏捷的委托。它需要各种星星以正确(错误)的方式排列。
首先,事件源必须将特定的语义应用到事件处理程序已经返回的事实中。例如,它可能会假设当事件处理程序返回时,某些操作已经被执行,例如在事件参数上设置响应属性。
大多数情况下,当这种情况发生时,事件参数提供了一个延迟。这让事件处理程序说:"好吧,我还没有完成,所以请等我再去做一些工作。等我做完了,我会告诉你的。" 为什么你需要这样做的原因是,如果你的一些工作涉及到coroutines。
object.SomethingHappened(
[=](auto sender, auto e) -> winrt::fire_and_forget
{
auto lifetime = get_strong();
auto deferral = e.GetDeferral();
co_await resume_foreground(Dispatcher());
e.Result(co_await calculate_result());
deferral.Complete();
});
(注意,由于我们是一个coroutine,所以我们是通过值而不是通过引用来接受参数的。)
但是,假设你的事件源没有对其事件参数提供延迟。我看着你,DeviceWatcher。
进一步假设你的委托人需要在特定的线程上下文中执行操作。(如果它不需要特定的线程上下文,那么委托人可以只在它被调用的任何上下文上做它的工作)。最常见的例子是一个响应事件而操作用户界面对象的委托人。
同时假设事件源在 "错误的 "线程上下文中引发了事件,也就是说,与你所需要的上下文不同。大多数与UI相关的事件都是在合适的UI线程上引发的,所以这通常只对那些不是UI事件,但你想在事件处理程序中做UI工作的事件有问题。
而需要使用这个技巧的最后一个假设是,你打算在特定线程上下文上执行的工作都是同步的。我们稍后会再看一下这个要求)。
如果所有的星星都(错误地)对齐,一个非敏捷的委托可能会很有用。你可以通过使用我在谈论COM上下文时介绍的InvokeInContext函数来创建一个,并使用我们在演示如何使用上下文返回COM公寓时使用的相同技术。
deviceWatcher.Added(
[=, context = CaptureCurrentApartmentContext()]
(auto&& sender, auto&& info)
{
InvokeInContext(context.Get(), [&]()
{
viewModel.Append(make<DeviceItem>(info));
});
});
请注意,参数可以通过引用接受,因为lambda是同步运行的。
下一次,我们将更仔细地研究这个模式。
额外的唠叨。因为我们实际上并没有创建一个非敏捷的委托。相反,我们创建了一个敏捷委托,它的行为就像一个非敏捷委托。这基本上是一样好的。