2015-11-06 105 views
3

我的问题:我想在.NET 4和的WinForms应用程序中使用TPL我需要任务延续立即提升所有未处理的异常(“快速抛出”),而不是等待GC收集Task这可能吗?在TPL中快速抛出未处理的异常

在.NET 4.5与async/await支持,可以这样写:

 
Public Class AwaitForm 
    Inherits Form 

    Private Async Sub Execute() 
     Dim uiScheduler = TaskScheduler.FromCurrentSynchronizationContext() 

     Try 
      Await Me.LongWork(). 
       ContinueWith(Sub(t) Me.LongWorkCompleted(), uiScheduler) 

     Catch ex As Exception 
      ' yay, possible to handle here 
      ' eg. MsgBox(ex.Message) 
      Throw 
     End Try 
    End Sub 

    Private Async Function LongWork() As Task 
     Await Task.Delay(1000) 
    End Function 

    Private Sub LongWorkCompleted() 
     Throw New Exception("Ups") 
    End Sub 

End Class 

在延续的例外是,如果在Excecute方法处理不立即抛出。

如何在不支持async/await的情况下在.NET 4中实现相同的行为?

回答

1

首先,你应该知道这是可能使用异步等待与.net 4.0 Microsoft.Bcl.Async

但是,如果没有它,你可以继续添加到任务与ContinueWith并有它,只有当有运行与TaskContinuationOptions.OnlyOnFaulted

Me.LongWork().ContinueWith(Sub(task) MsgBox(task.Exception.Message), CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted) 
+0

这不能回答我的问题 - 我想“快速抛出”任何未处理的异常。也许我因为处理程序的例子而引入了一些混淆。但想象一下,没有处理程序。或者任何异常处理程序总是可能会引发异常本身。我希望这些异常可以立即抛出,而不需要TPL将它们吞并为UnobservedTaskException。 – mancze

+0

@mancze抛出哪里?你想拆除应用程序? – i3arnon

+0

通过整个调用堆栈提升和冒泡异常。可能有我的自定义处理程序正确处理它的方式。没有处理程序拆除应用程序,是的。 – mancze

1

1除外)它或者可以使用作为Microsoft.Bcl.Async建议i3arnon。

2)或者如果你不想引用额外的库,我想出了基于async/await的解决方案。背后的魔力很糟糕,但最好的是我拥有。

Imports System.Reflection 
Imports System.Runtime.CompilerServices 
Imports System.Threading 


Public Module TaskExtensions 

    ''' <summary>Throws the exception on the current SynchronizationContext or ThreadPool if there is none.</summary> 
    ''' <param name="task">Task whose faulted continuation should throw exception.</param> 
    <Extension()> 
    Public Sub ThrowOnFaulted(task As Task) 
     Dim context = SynchronizationContext.Current 
     ThrowOnFaulted(task, context) 
    End Sub 


    ''' <summary>Throws the exception on the ThreadPool in given context.</summary> 
    ''' <param name="task">Task whose faulted continuation should throw exception.</param> 
    ''' <param name="targetContext">The target context on which to propagate the exception. Null to use the ThreadPool.</param> 
    <Extension()> 
    Public Sub ThrowOnFaulted(task As Task, targetContext As SynchronizationContext) 
     task.ContinueWith(Sub(t) ThrowOnFaultedCore(t, targetContext), TaskContinuationOptions.OnlyOnFaulted) 
    End Sub 


    ''' <remarks>Taken from System.RunTime.CompilerServices.AsyncServices.</remarks> 
    Private Sub ThrowOnFaultedCore(task As Task, targetContext As SynchronizationContext) 
     Dim exception = task.Exception 

     If targetContext IsNot Nothing Then 
      Try 
       targetContext.Post(Sub(state) Throw DirectCast(state, Exception), exception) 
       Return 
      Catch ex As Exception 
       exception = New AggregateException({exception, ex}) 
      End Try 
     End If 

     ThreadPool.QueueUserWorkItem(Sub(state) Throw DirectCast(state, Exception), exception) 
    End Sub 

End Module 

它符合要求 - 异常被抛出“快速”并且可以被处理。例外是Post编辑到目标SynchronizationContext因此逃避TPL的异常捕获机制。它远没有快速和同步,但至少比等待任务处理更好。