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

260 阅读1分钟

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

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

发布时间:2020年7月23日

我们上次看到,你可以通过轮询取消来加速C++/WinRT的coroutine的取消。但这只有在需要响应取消的是顶层的coroutine时才有效。但通常情况下,你的coroutine会调用其他coroutine,如果这些其他coroutine中的一个需要很长时间,你的主coroutine就没有机会响应取消,直到它重新获得控制权。

我们可以让主 coroutine 更快地响应取消吗?

当然可以。

你可以传递一个自定义的委托给C++/WinRT取消标记的回调方法,以便在coroutine被取消时立即得到通知。

IAsyncAction ProcessAllWidgetsAsync()
{
    auto cancellation = co_await get_cancellation_token();

    cancellation.callback([] { /* zomg! cancelled! */ });

    auto widgets = co_await GetAllWidgetsAsync();
    for (auto&& widget : widgets) {
        if (cancellation()) co_return;
        ProcessWidget(widget);
    }
    co_await ReportStatusAsync(WidgetsProcessed);
}

当coroutine被取消时,会立即调用取消回调。这是你加速你的coroutine死亡的机会。例如,我们可以通过取消GetAllWidgetsAsync调用来实现。

IAsyncAction ProcessAllWidgetsAsync()
{
    auto cancellation = co_await get_cancellation_token();

    auto operation = GetAllWidgetsAsync();
    cancellation.callback([operation] { operation.Cancel(); });
    auto widgets = co_await operation;

    for (auto&& widget : widgets) {
        if (cancellation()) co_return;
        ProcessWidget(widget);
    }
    co_await ReportStatusAsync(WidgetsProcessed);
}

如果ProcessAllWidgetsAsync被取消,我们就会把这个取消传播给GetAllWidgetsAsync操作,希望它放弃获取所有widgets的尝试,把控制权还给ProcessAllWidgetsAsync。co_await会以hresult_canceled失败,然后会从coroutine中传播出去,导致整个coroutine被取消。

这是个很常见的模式,你可以为它写一个包装器。

template<typename Async, typename Token>
std::decay_t<Async> MakeCancellable(Async&& async, Token&& token)
{
    token.callback([async] { async.Cancel(); });
    return std::forward<Async>(async);
}

现在我们只需将异步操作封装在一个MakeCancellable里面。

IAsyncAction ProcessAllWidgetsAsync()
{
    auto cancellation = co_await get_cancellation_token();

    auto widgets = co_await MakeCancellable(GetAllWidgetsAsync(), cancellation);

    for (auto&& widget : widgets) {
        if (cancellation()) co_return;
        ProcessWidget(widget);
    }
    co_await MakeCancellable(ReportStatusAsync(WidgetsProcessed), cancellation);
}

练习。如果在GetAllWidgets完成后,ProcessAllWidgetsAsync被取消会怎样?


www.deepl.com 翻译