在 TPL 中快速抛出未处理的异常
Fast throw unhandled exceptions in TPL
我的问题:我想在 .NET 4 下的 WinForms 应用程序中使用 TPL,并且 我需要任务继续以立即提升任何未处理的异常 ("fast throw") 而不是等待 GC
收集 Task
. 可能吗?
在支持 async/await
的 .NET 4.5 中,可以这样写:
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
方法中处理,continuation中的异常会立即抛出。
如何在没有 async/await
支持的情况下在 .NET 4 中实现相同的行为?
首先,您应该知道在 .Net 4.0 Microsoft.Bcl.Async
中可以使用 async-await
但是如果没有它,您可以使用 ContinueWith
为任务添加一个延续,并且仅当 TaskContinuationOptions.OnlyOnFaulted
出现异常时才使用它 运行
Me.LongWork().ContinueWith(Sub(task) MsgBox(task.Exception.Message), CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted)
1) 可以按照 i3arnon 的建议使用 Microsoft.Bcl.Async
。
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
它符合要求 - 抛出异常 "fast" 并且可以处理。异常被 Post
ed 到目标 SynchronizationContext
从而逃避了 TPL 的异常捕获机制。它远谈不上快速和同步,但至少它比等待任务处理表现得更好。
我的问题:我想在 .NET 4 下的 WinForms 应用程序中使用 TPL,并且 我需要任务继续以立即提升任何未处理的异常 ("fast throw") 而不是等待 GC
收集 Task
. 可能吗?
在支持 async/await
的 .NET 4.5 中,可以这样写:
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
方法中处理,continuation中的异常会立即抛出。
如何在没有 async/await
支持的情况下在 .NET 4 中实现相同的行为?
首先,您应该知道在 .Net 4.0 Microsoft.Bcl.Async
但是如果没有它,您可以使用 ContinueWith
为任务添加一个延续,并且仅当 TaskContinuationOptions.OnlyOnFaulted
Me.LongWork().ContinueWith(Sub(task) MsgBox(task.Exception.Message), CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted)
1) 可以按照 i3arnon 的建议使用 Microsoft.Bcl.Async
。
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
它符合要求 - 抛出异常 "fast" 并且可以处理。异常被 Post
ed 到目标 SynchronizationContext
从而逃避了 TPL 的异常捕获机制。它远谈不上快速和同步,但至少它比等待任务处理表现得更好。