c# - 使用任务保持 UI 响应,处理 AggregateException

标签 c# multithreading task aggregateexception

如果我的 WinForms 应用程序启动任务以在任务执行时保持响应,我在处理 AggregateException 时遇到问题。

简化的情况如下。 假设我的表单有一个相当慢的方法,例如:

private double SlowDivision(double a, double b)
{
    System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5));
    if (b==0) throw new ArgumentException("b");
    return a / b;
}

按下按钮后,我希望我的表单显示 SlowDivision(3,4) 的结果。 以下代码会使用户界面挂起一段时间:

private void button1_Click(object sender, EventArgs e)
{
    this.label1.Text = this.SlowDivision(3, 4).ToString();
}

因此我想启动一个任务来进行处理。当此任务完成时,它应该继续执行将显示结果的操作。为了防止出现 InvalidOperationException,我需要确保从创建它的线程访问 label1,因此需要一个 Control.Invoke:

private void button1_Click(object sender, EventArgs e)
{
    Task.Factory.StartNew ( () =>
    {
        return this.SlowDivision(3, 4);
    })
    .ContinueWith( (t) =>
    {
        this.Invoke( new MethodInvoker(() => 
        {
            this.label1.Text = t.Result.ToString();
        }));
    });
}

到目前为止一切顺利,但是如何处理异常,例如如果我想计算 SlowDivision(3, 0)?

通常,如果任务抛出未处理的异常,它会通过 AggregateException 转发到等待线程。大量示例显示以下代码:

var myTask = Task.Factory.StartNew ( () => ...);
try
{
    myTask.Wait();
}
catch (AggregateException exc)
{
    // handle exception
}

问题是:我迫不及待地等待我的任务执行,因为我希望我的 UI 保持响应。

在发生故障时创建一个任务继续,它将读取 Task.Exception 并相应地处理不起作用:

private void button1_Click(object sender, EventArgs e)
{
    var slowDivTask = Task.Factory.StartNew(() =>
    {
       return this.SlowDivision(3, 0);
    });

    slowDivTask.ContinueWith((t) =>
    {
        this.Invoke(new MethodInvoker(() =>
        {
            this.label1.Text = t.Result.ToString();
        }));
    }, TaskContinuationOptions.NotOnFaulted);

    slowDivTask.ContinueWith((t) =>
    {
        AggregateException ae = t.Exception;
        ae.Handle(exc =>
        {
            // handle the exception
            return true;
        });
    }, TaskContinuationOptions.OnlyOnFaulted);
}

函数中的 try/catch 也无济于事(正如预期的那样)。

那么我如何在不等待任务抛出的 AggregateExceptions 的情况下做出正确 react 。

最佳答案

如果你可以使用 .NET 4.5,那么我会使用更新的 async/await,它大大简化了代码,并且让你不必处理continuations 和 AggregateException,它们只会在代码中制造噪音,让您无法专注于您实际想要完成的事情。

它看起来像这样:

private async void button1_Click(object sender, EventArgs e)
{
    try
    {
        double result = await Task.Run(() => this.SlowDivision(3, 0));
        this.Label1.Text = result.ToString();
    }
    catch (Exception ex)
    {
        this.textBox1.Text = ex.ToString();
    }
}

private double SlowDivision(double a, double b)
{
    System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5));
    if (b == 0) throw new ArgumentException("b");
    return a / b;
}

关于c# - 使用任务保持 UI 响应,处理 AggregateException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31052523/

相关文章:

C# 生成随机 IP 地址

c# - 从 WSDL (.NET) 生成 SOAP 请求

c# - View 中的 WPF ReactiveUI 绑定(bind)

c# - 每个程序员都制作的软件

python - 条件锁定的通知后和释放前会发生什么

c# - 为异步 UI 消耗包装慢速同步 I/O

c# - C# (.NET) 中的 Android AsyncTask 类似功能

Python thread.join(time) 不返回

java - Android Activity 创建我的线程的多个实例

c# - 在异步操作后立即调用 Task.Wait() 是否等同于同步运行相同的操作?