我目前正在尝试将相当数量的现有同步代码移植到WinRT.
作为其中的一部分,我遇到了现有代码的问题,希望某些操作是同步的 – 例如用于文件I / O.
为了使这个现有代码适应WinRT中的IAsyncOperation样式API,我使用了一种使用扩展方法包装IAsyncOperation的技术,如:
namespace CirrIoUs.MvvmCross.Plugins.File.WinRT { public static class WinRTExtensionMethods { public static TResult Await<TResult>(this IAsyncOperation<TResult> operation) { var task = operation.AsTask(); task.Wait(); if (task.Exception != null) { // TODO - is this correct? throw task.Exception.InnerException; } return task.Result; } } }
从MvvmCross WinRT ExtensionMethods开始 – 使用与IAsyncAction类似的方法
这些包装器似乎有效 – 它们允许我在同步代码中使用Async方法,如:
public IEnumerable<string> GetFilesIn(string folderPath) { var folder = StorageFolder.GetFolderFromPathAsync(ToFullPath(folderPath)).Await(); var files = folder.GetFilesAsync().Await(); return files.Select(x => x.Name); }
我知道这并不是真正符合WinRT的精神;但我希望这些方法通常只能在后台线程上调用;我写这篇文章的目的是让我的代码跨平台兼容 – 包括那些尚未支持await-async的平台和/或尚未准备好跳转的开发人员.
所以……问题是:使用这种类型的代码我运行的风险是什么?
解决方法
首先,我认为您的方法可以改写为:
public static TResult Await<TResult>(this IAsyncOperation<TResult> operation) { return operation.AsTask().Result; }
如果任务尚未完成,则调用结果将同步等待.如果失败,它将抛出AgreggateException.我认为像你一样抛出InnerException是个坏主意,因为它会覆盖异常的堆栈跟踪.
关于你的实际问题,我认为使用Wait()和异步代码的最大危险是死锁.如果你在UI线程上启动一些内部使用await的操作,然后在同一个线程上使用Wait()等待它,你将遇到死锁.
如果你没有在UI线程上等待,那么这并不重要,但如果可能的话,你仍然应该避免它,因为它违背了整个异步的想法.