2016-03-13 45 views
1

我听说火&的异常忘记了异步呼叫被吞下。但是,我并不是这种情况,我用下面的例子:如何为异步/等待制定沉默异常?

class Program 
{ 
    static void Main(string[] args) 
    { 
     foo(); 
     Thread.Sleep(3000); 
    } 

    static async void foo() 
    { 
     Task.Factory.StartNew(() => 
      { 
       Thread.Sleep(1000); 
       throw new Exception(); 
      }); 
    } 
} 

抛出异常,导致快速失败。我不知道GC是否会在收集对象时引发异常。

在什么样的代码示例中,开发人员在异步/等待代码中遇到未观察到的异常异常?

+1

请注意,您的'async'方法没有在等待任何内容,所以您不应该将它标记为'async',因为编译器警告您正在告诉您。 – Servy

+0

@TomK:在您发布的代码中,异常*被*默默吞下。你可能只是看到调试器通知你这个异常。 –

回答

0

是否吞下任务异常取决于.NET版本和设置。从4.5开始,它们被默认忽略。不要更改该默认值。不要更改进程范围的设置。相反,与时俱进。

GC触发故障快速行为。不要依赖那个。

在你的例子中,你已经使用了异步void,它再次改变了行为。它向当前同步上下文发布异常。这里没有。不要使用异步无效。

在异步方法中嵌套Task.Factory.StartNew会创建一个完全独立的任务。你的异步方法没有意义。

0

当正在运行的任务内发生未处理的异常时,它会一直存储直到它被观察到。有几种方法可以观察未处理的任务异常。调用Wait(),得到Exception of Task对象或得到Result of Task<T>对象。

如果你不给故障任务的机会,传播它的例外>运行时将 根据当前 .NET异常策略升级任务的未观测到的异常当任务是垃圾回收。未观测到的 任务异常最终将在终结器 线程上下文中被观察到。如果在执行Finalize方法期间抛出未处理的异常,则默认情况下,运行时将终止当前进程 ,并且不会执行活动的try/finally块或执行其他终结器 ,包括释放处理为非托管的终结器资源。

这是从Parallel Programming with Microsoft .NET书。

但是,由于.NET 4.5的默认行为是未被观察到的异常被“吞噬”:不会在最终化上下文中抛出,也不会终止进程。它可以通过以下配置标志被改变:

<configuration> 
    <runtime> 
     <ThrowUnobservedTaskExceptions enabled="true"/> 
    </runtime> 
</configuration> 

异步/等待方法由编译器翻译成使用Task<T>对象的代码,特别await将被翻译成类似:

(.. everything before await...).ContinueWith(...the rest of the method...) 

在内部将打电话给上面提到的其中一个:Wait()Result并观察异常。但是当你用Task.RunTask.Factory.StartNew运行“火&忘记”任务(这不是这种情况,当你使用await时)并且从不等待或等待它,任何未处理的异常都将被忽略。所以,一般来说,这样做是不好的做法。