[Windows翻译]如何让你的C++/WinRT异步操作更快地响应取消,第3部分。

182 阅读2分钟

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

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

发布时间:2020年9月23日

早在如何让你的C++/WinRT异步操作更快地响应取消第二部分中,我介绍了一个帮助函数,它可以将一个coroutine的取消传播到它本身正在等待的coroutine中,这样整个事情就可以更快地取消。

此次发布的C++/WinRT 2.0.200917.4版包含了一个新的功能。自动传播取消。你可以阅读pull request进行深入了解。

对你这个应用开发者来说,它的工作方式是你可以在取消令牌上调用enable_propagation()方法。如果该coroutine在忙于co_wait另一个coroutine的时候被取消,那么该coroutine的取消就会被传播到被wait-for的coroutine中,从而加速了外层coroutine的死亡。

例如

IAsyncAction DoHttpThingAsync()
{
    auto cancellation = co_await get_cancellation_token();
    cancellation.enable_propagation();

    auto result = co_await httpClient.TryGetStringAsync(uri);

    if (result.Succeeded()) {
        DoSomethingWith(result.Value());
    }
}

由于启用了enable_propagation(),如果DoHttpThingAsync在等待TryGetStringAsync的结果时被取消,那么TryGetStringAsync操作就会立即被取消,而不是等待它自己完成,从而避免了非常长的网络超时。

enable_propagation()方法需要一个可选的bool参数,它指定了你是要启用取消传播(true,默认值)还是禁用它(false)。它还会返回之前的设置,所以你可以在需要的时候恢复它。

取消传播现在已经内置于C++/WinRT中,所以我们不需要在第二部分中看到的MakeCancellable帮助程序。

而且,取消传播支持 resume_on_signal 和 resume_after,所以你可以取消一个等待操作。这对于 resume_on_signal 很重要。如果你取消操作是因为内核对象永远不会发出信号(例如,因为你意识到你等待的事情永远不会发生),你需要一些方法让coroutine恢复(在取消的状态下),这样它就可以清理它的资源。否则,coroutine就会卡在一个永久中止的状态中,最后被泄露。

为了与以前的C++/WinRT版本兼容,自动取消传播在默认情况下被禁用。你必须通过调用enable_propagation()来选择。

额外的唠叨。取消传播的实现有点奇怪,因为它是针对从未发生取消的情况进行优化的。大部分昂贵的工作都发生在取消过程中,只有在每个co_await中执行了一点点记账。我通过在PR 171中从co_await路径中移除一个锁来支付这个额外的成本。

额外的奖励聊天。任何等待者都可以通过转换为winrt::enable_await_cancellation(通常通过继承它)并实现enable_cancellation()方法来参与取消传播。下面是对C++/WinRT等待器的分解。

支持取消传播-
IAsyncXxx, resume_after, resume_on_signal
apartment_context,resume_background,resume_foreground,thread_pool,wait_for_deferrals

线程切换等待器不支持取消传播,因为没有地方可以取消回去。原来的线程早已不在了。唯一的办法就是向前走,在目标线程上继续。


www.deepl.com 翻译