[Windows翻译]取消Windows Runtime异步操作,第1部分。C#

272 阅读2分钟

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

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

发布时间:2020年7月1日

Windows Runtime中有很多异步操作,典型的处理方式是在操作完成后安排继续。根据语言的不同,可能是Promise.then或task.then(),也可能是 await或co_await。它们都归结为 "立即从函数中返回,并在这个异步事物产生一个值时继续执行"。

Windows Runtime异步操作可以被取消,然后事情就变得很有趣了。

调用Cancel方法,从技术上讲,只是一个请求,而不是一个需求。建议操作支持取消,但并不是严格要求。一个操作可能会选择直接忽略你的取消请求。或者它可以通过使操作立即完成部分结果来处理取消请求。

但是,让我们假设操作接受了你的取消请求,并且确实以Canceled的状态完成了操作。

你的代码看到的取消结果是什么?

让我们从C#开始。为了取消操作,你必须将其包裹在一个任务中,然后取消任务。

var picker = new FileOpenPicker { FileTypeFilter = { ".txt" } };
var cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromSeconds(3));

StorageFile file;
try {
    file = await picker.PickSingleFileAsync().AsTask(cts.Token);
} catch (TaskCanceledException) {
    file = null;
}

if (file != null) {
    DoSomething(file);
}

我们在三秒后取消文件拾取器对话框。这是由PickSingleFileAsync()返回的AsyncOperation来完成的,将其转换为一个带有AsTask的Task,并将其与一个已配置为三秒后取消的取消令牌源相关联。

当这个操作被取消时,你会得到一个TaskCanceledException。这是Windows Runtime异步操作完成时状态为Canceled的结果。然后,C#投影将其转换为TaskCanceledException。

这是C#异步代码所期望的行为,所以C#将Windows Runtime异步操作投射到任务中的行为很自然。

下一次,我们再来看看C++/CX与PPL。

额外的唠叨。你也可以通过直接与IAsyncOperation对话来取消任务,而不是将其转换为C#任务。

async void CancelAfter(IAsyncInfo info, TimeSpan delay)
{
    await Task.delay(delay);
    info.Cancel();
}

var picker = new FileOpenPicker { FileTypeFilter = { ".txt" } };
StorageFile file;
try {
    var op = picker.PickSingleFileAsync();
    CancelAfter(op, TimeSpan.FromSeconds(3));
    file = await op;
} catch (TaskCanceledException) {
    file = null;
}

if (file != null) {
    DoSomething(file);
}

你可以尝试通过将CancelAfter代码内联来赚取一些风格点。

var picker = new FileOpenPicker { FileTypeFilter = { ".txt" } };
StorageFile file;
try {
    var op = picker.PickSingleFileAsync();
    ((Action)(async () => { await Task.Delay(TimeSpan.FromSeconds(3)); op.Cancel(); }))();
    file = await op;
} catch (TaskCanceledException) {
    file = null;
}

或者也许更有用,让CancelAfter返回原来的异步操作,这样你就可以一次性取消它并等待它。

public static class Helpers
{
    static async void CancelAfterHelper(IAsyncInfo info, TimeSpan delay)
    {
        await Task.Delay(delay);
        info.Cancel();
    }

    static public IAsyncAction CancelAfter(this IAsyncAction action, TimeSpan delay)
    {
        CancelAfterHelper(action, delay);
        return action;
    }

    static public IAsyncOperation<T>
        CancelAfter<T>(this IAsyncOperation<T> op, TimeSpan delay)
    {
        CancelAfterHelper(op, delay);
        return op;
    }
}

var picker = new FileOpenPicker { FileTypeFilter = { ".txt" } };
StorageFile file;
try {
    file = await picker.PickSingleFileAsync().CancelAfter(TimeSpan.FromSeconds(3));
} catch (TaskCanceledException) {
    file = null;
}

www.deepl.com 翻译