为什么第一个代码会立即显示错误消息,但第二个代码不会

时间:2016-10-26 20:32:38

标签: c# garbage-collection task-parallel-library

假设我们有两个按钮点击:

private void button2_Click(object sender, EventArgs e)
{
        using (
            System.Threading.Tasks.Task t = new System.Threading.Tasks.Task(() => {
                throw new Exception("puzzle");

            }))
        {
            t.Start();
        }
}

private void button3_Click(object sender, EventArgs e)
{
        System.Threading.Tasks.Task t = new System.Threading.Tasks.Task(() => {
                                  throw new Exception("puzzle");

        });
        t.Start();
}

如果在没有调试的情况下使用这两个事件处理程序运行程序,那么button2_Click会立即显示错误消息。但button3_Click不会立即显示错误消息。我有一个印象,如果Task被垃圾收集,那么它会抛出异常。如果Task没有被垃圾收集,那么会生成异常,但仍然存在于一些有趣的地方。我试图在MSDN上找到一些很好的例子,但不是很幸运。

2 个答案:

答案 0 :(得分:3)

我可以看到从其他按钮处理程序抛出错误。如果您在VS IDE中启用仅我的代码工具 - >选项 - >调试 - >一般),这将是显而易见的

在我解释为什么你可以从button2处理程序看到错误以及为什么不从button3看到错误之前,了解.NET如何处理任务异常是有意义的。

首先,您的两个Task操作都会抛出代码中的unhandled异常行为。当涉及到Task时,用户代码抛出的任何unhandled exceptions都会以AggregateException的形式传播回调用线程;只有当您Wait Task(例如异常)或调用Result来获取输出时,才会引发此异常。在你的情况下,你没有做这些中的任何一个,因此你的例外不被注意。但是这些异常仍然存在,直到任务garbage collected并且将根据.NET异常策略升级。如果要覆盖异常升级,则需要查看TaskScheduler.UnobservedTaskException

现在回到你的处理程序;

如果是button2:您要求在启动任务后立即执行确定性销毁(using block)。这意味着,您的任务可能位于WaitingToRunWaitingForActivationRunningRunning等状态。根据MSDN,只有在任务处于完成状态(RanToCompletion, Faulted or Canceled)时才可以处置该任务。 话虽如此,异常在所有情况下可能并不明显,因为它取决于您的任务状态以及确定性破坏开始的时刻。

如果是button3:您没有自行处理Task并将其留给垃圾收集器为您执行此操作。这就是为什么你看不到任何异常的原因。我希望你能清楚地知道如何处理异常。

MSDN详细解释了如何在.NET中处理TASK异常,并涵盖多种场景。

答案 1 :(得分:1)

我用VS2015测试了你的代码,使用button2,我得到了例外:

System.InvalidOperationException: A task may only be disposed if it is in a completion state (RanToCompletion, Faulted or Canceled).

任务处于故障状态是正确的。

使用button3,我什么也没得到。这也是正确的行为:t.start()不是块调用,函数button3_Click简单结束。如果等待该任务,则会收到AggregateException异常。 Exceptions are propagated when you use one of the static or instance Task.Wait or Task.Wait methods