【问题标题】:Can you explain why the exception is not caught if I do not await an async task?如果我不等待异步任务,您能否解释为什么没有捕获异常?
【发布时间】:2012-06-05 10:17:00
【问题描述】:

从我在代码中遇到的问题开始,我创建了这个简单的应用程序来重现问题:

private async void button1_Click(object sender, EventArgs e)
{
    Task task = Task.Run(() =>
    {
        TestWork();
    });
    try
    {
        await task;
        MessageBox.Show("Exception uncaught!");
    }
    catch (Exception) { MessageBox.Show("Exception caught!"); }
}

private async void button2_Click(object sender, EventArgs e)
{
    Task task = TestWork();
    try
    {
        await task;
        MessageBox.Show("Exception uncaught!");
    }
    catch (Exception) { MessageBox.Show("Exception caught!"); }
}

private async Task TestWork()
{
    throw new Exception();
}

button1_Click 的代码不会捕获异常。我已经验证这是因为我没有等待 TestWork async 方法。事实上,我收到了来自 Visual Studio 的警告消息,通知我我没有等待该方法。但是,解决方案可以编译,如果我广泛使用 async/await,我担心这会发生在我的代码中的其他地方。那么您能否解释一下原因,并给出一些避免它的黄金法则?

P.S.:如果在 button1_Click 我写的代码中,它可以工作:

Task task = Task.Run(async () =>
{
    await TestWork();
});

【问题讨论】:

  • 另一种正确编写button1_Click 代码的方法是Task.Run(() => TestWork())

标签: c# exception async-await uncaught-exception


【解决方案1】:

在您的第一个button1_Click 实现中,您忽略了TestWork 返回的Task 的结果。

在您的修改版本中,await 检查异常并传播它以供您捕获。

事实上,您看到的编译器警告通常很重要。如果TestWork 实际上在不同的线程上运行了某些东西,那么因为您的第一个实现中的包装器Task 不会等待TestWork 完成,所以它会在TestWork 开始时自行完成。

这样写更清楚:

Task task = Task.Run( () =>
    {
        Task t = TestWork();
        // ignore t
    }
);

【讨论】:

  • @Adriano 仅仅因为一个方法是async 并不意味着它会在线程池上运行!事实上,TestWork() 不会在那里运行的原因有两个: 1. 它不包含await,这意味着它将完全同步运行。 2. 它在UI线程上运行,这意味着await之后的任何代码都将在UI线程上运行,而不是在池上。
  • 我不确定未使用的变量是否更清晰,这两种情况都需要注释。
  • @Adriano 你完全错了。这根本不是async 的行为方式,我认为您应该先阅读更多内容,然后再提出这样的声明。如果您不相信我或someone from the C# team,请自己尝试一下。
  • @svick 你是对的(谁说我不相信你?),谢谢你的澄清链接!!!
猜你喜欢
  • 2017-03-23
  • 2021-03-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-01
  • 1970-01-01
相关资源
最近更新 更多