我的问题:我想在.NET 4下的WinForms应用程序中使用TPL,并且我需要任务延续来立即提升任何未处理的异常("快速抛出")而不是等待{{ 1}}收集GC
。有可能吗?
在支持Task
的.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
如果未在async/await
方法中处理,则会立即抛出延续中的异常。
如何在没有Excecute
支持的情况下在.NET 4中实现相同的行为?
答案 0 :(得分:1)
首先,您应该知道可以将.Net 4.0与Microsoft.Bcl.Async
但如果没有它,您可以使用ContinueWith
为任务添加续集,并仅在TaskContinuationOptions.OnlyOnFaulted
Me.LongWork().ContinueWith(Sub(task) MsgBox(task.Exception.Message), CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted)
答案 1 :(得分: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的异常捕获机制。它远非快速和同步,但至少它比等待任务处理更好。