在TPL中快速抛出未处理的异常

时间:2015-11-06 09:23:26

标签: .net vb.net asynchronous task-parallel-library

我的问题:我想在.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中实现相同的行为?

2 个答案:

答案 0 :(得分:1)

首先,您应该知道可以将.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 :(得分: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的异常捕获机制。它远非快速和同步,但至少它比等待任务处理更好。