之前展示的是调用一个异步winrt api的常见场景,但之前的代码忽略了取消和进度更新。为了合理地处理取消和进度更新,就不显式让编译器调用Getawaiter 扩展方法了,而是用WindowsruntimeSystemextensions 类提供的AsTask扩展方法
namespace System {
public static class WindowsRuntimeSystemExtensions {
public static Task AsTask<TProgress>(this IAsyncActionWithProgress<TProgress> source,
CancellationToken cancellationToken, IProgress<TProgress> progress);
public static Task<TResult> AsTask<TResult, TProgress>(
this IAsyncOperationWithProgress<TResult, TProgress> source,
CancellationToken cancellationToken, IProgress<TProgress> progress);
// Simpler overloads not shown here
}
}
这样就可以加入取消和进度了,这是调用异步winrt api的代码:
using System; // For WindowsRuntimeSystemExtensions's AsTask
using System.Threading; // For CancellationTokenSource
internal sealed class MyClass {
private CancellationTokenSource m_cts = new CancellationTokenSource();
// NOTE: If invoked by a GUI thread, all code executes via that GUI thread:
private async void MappingWinRTAsyncToDotNet(WinRType someWinRTObj) {
try {
// Assume XxxAsync returns IAsyncOperationWithProgress<IBuffer, UInt32>
IBuffer result = await someWinRTObj.XxxAsync(...)
.AsTask(m_cts.Token, new Progress<UInt32>(ProgressReport));
// TODO: Completed code
}
catch (TaskCanceledException) { // Derived from OperationCanceledException
// TODO: Cancel code
}
catch (SomeOtherException) {
// TODO: Error code
}
}
private void ProgressReport(UInt32 progress) {
// Update progress code
}
public void Cancel() { m_cts.Cancel(); } // Called sometime later to cancel
这里有两个点值得一提。1,如果你不注意哪一个线程在await之后招行,你可能需要通过调用task的Configure- await方法改进程序的性能,传一个false给continueOnCapturedContext参数。2,这里有一些场景你可能想调用一个异步方法然后阻塞进程直接到操作完成。这些场景包括在构造函数中调用异步方法,比如:你有时候需要调用一个回调中的异步方法,一个重载了虚函数的方法。如果你将方法标记成async,调用你的方法的代码可能在异步方法完成前继续执行,而这通常是不想要的结果。另外,如果方法的返回类型是task或task<tresult>或void,你可以用async标记一个方法,因为很多delegate,virtual,interface方法的签名都没有这些类型,如果你的方法实现调用了一个异步方法,你必须阻塞线程。
下面的例子较好地调用了winrt的异步方法并阻塞了线程,直到操作完成:
StorageFile file = KnownFolders.MusicLibrary.GetFileAsync("Song.mp3").AsTask().GetAwaiter().GetResult();
AsTask方法将返回的对象转换成了.NET的task或task<tresult>对象。然后Getawaiter 方法返回了一个taskawaiter 对象知道如何等待操作完成。它的Getresult 方法阻塞了调用线程直到操作完成,而后要么返回对象要么抛出异常。
虽然你可以这样写代码,但是你不应该写成这样:StorageFile file = KnownFolders.MusicLibrary.GetFileAsync("Song.mp3").AsTask().Result;
原因是如果查询task的result属性操作失败不会抛出正确的异常而抛出aggregateexception异常。要知道通过getawaiter().getresult()阻塞一个GUI线程可能造成潜在的死锁,强制用户或操作系统终止app,所以你应该尽可能地避免阻塞一个导致异步操作的线程