WinRT中的异步操作

在.net framework 4.5 中,所有异步操作的系统函数返回值为一般都是Task 或Task<TResult> ,但在WinRT中,所有的系统函数异步操作都源自于一个接口IAsyncInfo,

    public interface IAsyncInfo
    {
        Exception ErrorCode { get; }
        uint Id { get; }
        AsyncStatus Status { get; }
        void Cancel();
        void Close();
    }

从这个接口上来看,它提供了取消和关闭任务的功能,并且能查看异步操作的执行状态,除了Status函数外,其它几个接口和TAP模式下的异步操作并没有直接关系,并且缺少await所需的GetResult()和OnCompleted()接口或能实现其功能的等价接口。也就是说,这个接口是不支持await的,对IAsyncInfo调用await会报编译错误。

虽然IAsyncInfo接口并本身不支持异步功能,实际上实际上我们使用的是它的四个子类,他们提供了GetResult()和OnCompleted()接口的等价声明:

    public interface IAsyncAction : IAsyncInfo
    {
        AsyncActionCompletedHandler Completed { get; set; }
        void GetResults();
    }

    public interface IAsyncOperation<TResult> : IAsyncInfo
    {
        AsyncOperationCompletedHandler<TResult> Completed { get; set; }
        TResult GetResults();
    }

    public interface IAsyncActionWithProgress<TProgress> : IAsyncInfo
    {
        AsyncActionWithProgressCompletedHandler<TProgress> Completed { get; set; }
        AsyncActionProgressHandler<TProgress> Progress { get; set; }
        void GetResults();
    }

    public interface IAsyncOperationWithProgress<TResult, TProgress> : IAsyncInfo
    {
        AsyncOperationWithProgressCompletedHandler<TResult, TProgress> Completed { get; set; }
        AsyncOperationProgressHandler<TResult, TProgress> Progress { get; set; }
        TResult GetResults();
    }

从它们的声明来看,IAsyncAction等价于不带返回值的异步任务Task,IAsyncOperation<TResult>等价带返回值的异步任务Task<T>。另外,还提供了两个带进度报告的版本:IAsyncActionWithProgress和IAsyncOperationWithProgress。

虽然这四个接口本身都没有声明GetAwaiter(),但WinRT类库WindowsRuntimeSystemExtensions中定义了相应的GetAwaiter扩展函数,因此它们是支持await操作的,返回值从其接口GetResults()中获取。

    public static class WindowsRuntimeSystemExtensions
    {
        public static TaskAwaiter GetAwaiter(this IAsyncAction source);
        public static TaskAwaiter<TResult> GetAwaiter<TResult>(this IAsyncOperation<TResult> source);
        public static TaskAwaiter GetAwaiter<TProgress>(this IAsyncActionWithProgress<TProgress> source);
        public static TaskAwaiter<TResult> GetAwaiter<TResult, TProgress>(this IAsyncOperationWithProgress<TResult, TProgress> source);
    }

在使用await操作时,我们往往还需要通过async关键字来配套使用,将多个await操作来重新封装成一个新的异步函数。在.net 4.5中,我们往往这么使用:

    public async Task AsyncOperation()
    {
        await ...
        return;
    }

由于WinRT的异步返回值是IAsyncInfo,那么我们是不是该把Task换成等价的IAsyncOperation呢?

    public async IAsyncAction AsyncOperation()
    {
        await ...
        return;
    }

实际上,当我们这么写时,会得倒一个"异步方法的返回类型必须为 void、Task 或 Task<T>"的编译错误。也就是说,在WinRT中,异步操作模型和.Net 4.5是一致的,本身还是基于Task的异步编程。

既然Task仍然是WinRT的异步编程的一等公民,那为什么还要提供一个IAsyncInfo呢?让我们再回头看看IAsyncInfo接口,发现他具有Task所不支持的两个接口:Cancel和Close。

  • 对于Close函数的功能,MSDN中语焉不详,没看到明确说要在什么地方用和注意的地方。但有一点值得一提的是:在.Net 4.5的IO操作中,虽然读写提供了异步接口,但Open却是同步的,但WinRT中的Open也是异步的,通过IAsyncInfo.Close函数可以直接关闭异步打开的资源。但这个并非一定要提供一个Close接口不可,返回的TResult对象里一般也提供了Close接口,大可以用它来关闭资源。
  • 对于Cancel函数,这个功能是Task无法直接实现的,必须和CancellationTokenSource配合使用,而IAsyncInfo本身就提供了这个功能。

从上可以看出:IAsyncInfo(的四个子接口)其实是个Task的增强版(支持Cancel和Close),WinRT系统的异步函数返回值提供的功能更加强大。

既然IAsyncInfo比Task更加强大,其自然也是可以转换成Task的,在WindowsRuntimeSystemExtensions类中就提供了扩展函数AsTask()实现把IAsyncInfo(的四个子类)转换为Task,从而使用Task的各个功能函数,使之更加强大,例如:

  • 实现GetAwaiter()扩展函数,使得IAsyncAction系列接口支持await操作(这个是前面提及的系统已经实现了的)。

        public static TaskAwaiter GetAwaiter(this IAsyncAction source)
        {
            return source.AsTask().GetAwaiter();
        }

  • 支持异步操作后不返回UI线程

        await SomeMethodAsync().AsTask().ConfigureAwait(false);

  • 任务取消

        CancellationToken token = ...;
        await SomeMethodAsync().AsTask(token);

  • 进度报告

        IProgress<TProgress> progress = ...;
        await SomeMethodAsync().AsTask(progress);

  • 其它Task函数的接口

        Task firstCompleted = await Task.WhenAny(
            SomeMethod1Async().AsTask(),
            SomeMethod2Async().AsTask(),
            SomeMethod3Async().AsTask());

既然知道了如何使用IAsyncInfo,除了系统自带的函数,我们如何创建自己的IAsyncInfo呢?常用方法有如下两种:

  1. 通过AsyncInfo.Run运算密集型的同步函数封装为返回值为IAsyncInfo的异步函数,这个非常类似于Task.Run
  2. 通过扩展方法AsAsyncAction 和 AsAsyncOperation 将Task转换为相应的IAsyncInfo,这个主要用于封装async关键字生产返回值为Task的异步函数。

以上只是对WinRT的异步操作进行了一个简单的介绍,如果想更加详细的了解相关知识,请参考MSDN文章:深入探究 WinRT 和 await将 .NET 任务作为 WinRT 异步操作公开

 

你可能感兴趣的:(异步)