使用Task.Wait()代码封装异步/等待IAsyncOperations有哪些风险?

问题描述:

我正在尝试将相当数量的现有同步代码移植到WinRT。作为其中的一部分,我遇到了一些问题,希望现有的代码能够实现某些操作是同步的 - 例如,文件I/O使用Task.Wait()代码封装异步/等待IAsyncOperations有哪些风险?

为适应这一现有代码中的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的精神,但我希望这些方法通常只能在后台线程中调用;而且我正在编写这个程序,目的是让我的代码跨平台兼容 - 包括尚未支持等待的平台 - 异步和/或尚未准备好跳转的开发人员。

所以......问题是:我使用这种类型的代码运行的风险是什么?

而作为第二个问题,有没有更好的方法可以实现代码重用,如File I/O?

+0

http://feedproxy.google.com/~r/AyendeRahien/~3/71OP6uo3bTQ/when-using-the-task-parallel-library-wait-is-a-bad-warning-sign –

我终于要回答这个....

答案是你不能真正做到这一点。

即使您尝试使用一些在其他的答案中建议的清洁方法,那么你最终还是打的异常,如果您尝试运行已承诺不会阻止任何线程的代码 - 例如如果您尝试在UI线程或线程池线程上运行。

所以......答案是,你只需要重新构建旧代码,以便它以某种方式异步!

首先,我觉得你的方法可能会被改写为:

public static TResult Await<TResult>(this IAsyncOperation<TResult> operation) 
{ 
    return operation.AsTask().Result; 
} 

调用Result将同步等待,如果任务没有完成尚未。如果失败,它会抛出AgreggateException。我认为像你这样抛出InnerException是一个坏主意,因为它会覆盖异常的堆栈跟踪。

关于您的实际问题,我认为使用Wait()与异步代码一起最大的危险是死锁。如果您在UI线程上启动了一些内部使用await的操作,然后您在同一线程上使用Wait()等待它,则会发生死锁。

这并不重要,如果你不是Wait()在UI线程上,但你应该尽可能避免它,因为它违背了整个async的想法。

+0

感谢您的回答。关于异常问题,我试图让周围的代码期望像FileNotFoundException这样的异常 - 我真的不希望他们处理AggregatedException。总体而言,虽然,这是* *希望由我一个权宜之计 - 在较长时期内,我会要么重写API来使用基于行动回调,或者也许我会在其他平台上也使用的await /异步(支持当VS11/C#4.5出来公测来的MonoTouch和MonoDroid的!) – Stuart

+0

FWIW,他似乎更符合的await关键字,因为它得到抛出,而不是总的内部异常。 –

有很多很好的理由不这样做。例如见http://blogs.msdn.com/b/pfxteam/archive/2012/04/13/10293638.aspx

但是,如果你想这样做,使用GetResults()方法如下

public static TResult Await<TResult>(this IAsyncOperation<TResult> operation) 
{ 
    try 
    { 
     return operation.GetResults(); 
    } 
    finally 
    { 
     operation.Close(); 
    } 
} 

在任务中包装IAsyncOperation,如前所述svick,工作太多,但效率不高。

+1

我无法得到这个工作。我尝试以下的代码: VAR夹= ApplicationData.Current.LocalFolder; var items = folder.CreateItemQuery()。GetItemsAsync()。Await(); 并得到: GetResults中发生了类型'System.InvalidOperationException'的异常 。更多信息: 一种方法被称为在一个意想不到的时间。 (来自HRESULT的异常:0x8000000E)。 AsTask()。结果奏效。 –

+0

我有同样的问题,是我找到这篇文章的原因。 GetResults()不起作用,但AsTask()。结果工作正常 – noggin182