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

Posted

技术标签:

【中文标题】在 TPL 中快速抛出未处理的异常【英文标题】:Fast throw unhandled exceptions in TPL 【发布时间】:2016-02-07 09:32:19 【问题描述】:

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

在支持async/await 的.NET 4.5 中,可以编写:

公开课AwaitForm 继承表格 私有异步子执行() Dim uiScheduler = TaskScheduler.FromCurrentSynchronizationContext() 尝试 等我。LongWork()。 ContinueWith(Sub(t) Me.LongWorkCompleted(), uiScheduler) 抓住前任作为例外 ' 是的,可以在这里处理 '例如。消息框(例如消息) 扔 结束尝试 结束子 私有异步函数 LongWork() 作为任务 等待任务。延迟(1000) 结束功能 私有子 LongWorkCompleted() 抛出新异常(“Ups”) 结束子 结束类

如果没有在Excecute方法中处理,将立即抛出继续中的异常。

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

【问题讨论】:

【参考方案1】:

首先,您应该知道可以将 async-await 与 .Net 4.0 与 Microsoft.Bcl.Async 一起使用

但如果没有它,您可以使用 ContinueWith 向任务添加延续,并仅在出现 TaskContinuationOptions.OnlyOnFaulted 异常时运行它

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

【讨论】:

这不能回答我的问题——我想“快速抛出”任何未处理的异常。也许我因为带有处理程序的示例而引入了一些混淆。但是想象一下没有处理程序。或者任何异常处理程序总是有可能自己引发异常。我希望立即抛出这些异常,而 TPL 不会将它们作为 UnobservedTaskException 吞下。 在整个调用堆栈中提升和冒泡异常。途中可能有我的自定义处理程序可以正确处理它。没有处理程序拆除应用程序,是的。 @mancze 只要没有线程等待它,任务就没有调用堆栈可以冒泡。您可以使用Task.Wait 阻止任务,但随后使用 TPL 开始将毫无意义。 我明白了,您已经明白了该任务本身没有特定的调用堆栈。除非我将任务安排到特定线程(ui 线程)。调用 Wait 可以解决问题,但会阻止我不想这样做的 ui 线程。我想尽快“Wait”,因为抛出异常。继续使用Wait 是不行的,因为再次 - 它的未处理异常将被 TPL 捕获。 我很乐意将您的答案标记为解决方案,因为您正确指出可以通过Microsoft.Bcl.Async 添加async/await 支持。它帮助我找到了自己的解决方案。如果您能强调答案的这一部分,我会很高兴。后面的部分不太相关。【参考方案2】:

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

它符合要求 - “快速”抛出异常并且可以处理。异常被Posted 到目标SynchronizationContext,从而逃避了TPL 的异常捕获机制。它远非快速和同步,但至少它比等待任务处理表现得更好。

【讨论】:

以上是关于在 TPL 中快速抛出未处理的异常的主要内容,如果未能解决你的问题,请参考以下文章

Xcode升级后iOS项目抛出未处理的异常

Visual C ++ - 从设置表单图标中抛出未处理的异常?

抛出未处理的异常:读取访问冲突。 x 是 0x20B4112

C++ Battle4Zion 项目抛出未处理的异常:读取访问冲突。 **this** 是 nullptr。发生了

抛出未处理的异常:写访问冲突。 bunnies_array 是 0x5CB3CBA

Visual Studio 2019 c++latest random_device uniform_int_distribution 抛出未处理的异常