【问题标题】:Cancellation throwing unhandled exception in .Net取消在.Net中引发未处理的异常
【发布时间】:2018-05-05 07:17:00
【问题描述】:

这似乎是一个常见问题,但我还没有找到解决方案。我已经检查过这个 Cancelling a Task is throwing an exception

我的来电者:

    Private Async Sub btnTestTimer_Click(sender As Object, e As EventArgs) Handles btnTest.Click
    _cts = New CancellationTokenSource()
    Try
        Await Task.Run(AddressOf TestCancellationAsync).ConfigureAwait(False)
    Catch cx As OperationCanceledException
        MsgBox(String.Format("The following error occurred: {0}", cx.Message), MsgBoxStyle.Critical)
    Catch ex As Exception
        MsgBox(String.Format("The following error occurred: {0}", ex.Message), MsgBoxStyle.Critical)
    End Try
End Sub

我的任务来了

    Private Async Function TestCancellationAsync() As Task
        'Launch a dummy timer which after some time will itself cancel a token and throw
        Dim tmr As New System.Timers.Timer(1000)
        AddHandler tmr.Elapsed, AddressOf OnTimerElapsed
        tmr.Enabled = True
    End Function

而取消和抛出的定时器函数是

    Private Sub OnTimerElapsed(sender As Object, e As ElapsedEventArgs)
        Dim tmr As System.Timers.Timer = CType(sender, System.Timers.Timer)
        tmr.Enabled = False
        Task.Delay(5000) 'After 5 seconds simulate a cancellation
        _cts.Cancel() //This is just to cancel from within the timer, actually the cancellation to _cts will happen from another caller which is not shown here
        _cts.Token.ThrowIfCancellationRequested()
    End Sub

这里没有显示带有异步任务和取消的实际程序,以保持示例简洁,同时仍然能够复制问题。

业务需求是点击一个按钮,会启动一个异步任务,会打开几个异步函数。其中之一将启动一个计时器,该计时器将继续检查 _cts 令牌状态并在需要时取消。如果在 _cts 令牌上从外部发生此类取消,则计时器将引发取消异常

我尝试过的事情:

  • 我已经处理了 OperationCancelled 异常,但它仍然没有出现。
  • 我已取消选中工具-选项-调试-常规-仅启用我的代码以查看它是否只是 Visual Studio。但它仍然被报告为 PDB 未处理的异常
  • 我已经从外部运行了 exe,正如预期的那样,由于未处理的异常,它崩溃了

请让我知道我在这里做错了什么。我的调用者等待任务完成 - 由于计时器正在从任务内部运行,我预计任务尚未完成,并且会捕获任何引发的异常。

【问题讨论】:

  • 谁否决了这个问题,请注意解释原因

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


【解决方案1】:

我认为在这种情况下,计时器是问题所在。在任务中创建并让定时器触发,然后从定时器处理方法中抛出取消异常不起作用,因为一旦创建并启用定时器,任务就会返回而无需等待定时器完成。这意味着TestCancellationAsync 方法不会等待计时器代码触发并hold 任务。它立即返回给调用者。 btnTestTimer_Click 内部的调用者认为任务已返回并退出尝试并结束方法。这意味着没有有效的事件处理程序来捕获计时器抛出的异常。这会导致未处理的异常。

解决方案是通过使用具有相关延迟的无限循环并在不创建计时器对象的情况下从内部调用计时器代码来模拟计时器。

所以TestCancellationAsync 应该改为如下所示

    Private Async Function TestCancellationAsync() As Task
    'Simulate a timer behaviour
    While True
        Await DoWorkAsync().ConfigureAwait(False)
        Await Task.Delay(1000).ConfigureAwait(False)
    End While
End Function

然后可以将实际上是工作函数的OnTimerElapsed更改为

Private Async Function DoWorkAsync() As Task
    'Do work code
    _cts.Token.ThrowIfCancellationRequested()
End Sub

现在 if _cts 从外部取消,它正在被捕获。

这解决了当前的问题。

【讨论】:

    猜你喜欢
    • 2013-01-29
    • 2013-10-09
    • 2014-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-15
    • 1970-01-01
    相关资源
    最近更新 更多