使用Tasks,Handle AggregateException保持UI响应

Har*_*lse 2 c# multithreading task aggregateexception

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

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

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

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

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

因此,我想开始执行处理任务。该任务完成后,应继续执行将显示结果的操作。为了防止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();
        }));
    });
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

问题是:我等不及执行任务,因为我希望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);
}
Run Code Online (Sandbox Code Playgroud)

尝试/捕获功能也无济于事(可以预料)。

因此,如何在不等待任务的情况下对任务引发的AggregateExceptions做出正确的反应。

sst*_*tan 6

如果可以使用.NET 4.5,那么我将使用较新的async/await,它可以大大简化代码,并使您不必处理延续和AggregateExceptions,因为它们只会在代码中产生干扰,使您无法专注于实际尝试执行的操作。完成。

它看起来像这样:

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;
}
Run Code Online (Sandbox Code Playgroud)