2015-11-06 41 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的異常捕獲機制。它遠沒有快速和同步,但至少比等待任務處理更好。