使用Tasks,Handle AggregateException保持UI响应 [英] Keep UI responsive using Tasks, Handle AggregateException

查看:95
本文介绍了使用Tasks,Handle AggregateException保持UI响应的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a problem with handling the AggregateException if my WinForms application starts a task to keep responsive while the task is performing.

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

The simplified case is as follows. Suppose my Form has a fairly slow method, for instance:

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)的结果. 以下代码会将用户界面挂起一段时间:

After pressing a button I want my form to show the result of SlowDivision(3,4). The following code would hang the user interface for some time:

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

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

Hence I'd like to start a task that will do the processing. When this task finishes, it should continue with an action that will display the result. To prevent an InvalidOperationException I need to be sure that label1 is accessed from the thread that it was created on, hence a 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)?

So far, so good, but how to handle exceptions, for instance If I want to calculate SlowDivision(3, 0)?

通常,如果任务引发未处理的异常,则会通过AggregateException将其转发给正在等待的线程.许多示例显示以下代码:

Normally if a task throws an unhandled exception, it is forwarded to a waiting thread via an AggregateException. Numerous examples show the following code:

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

问题是:我迫不及待要执行Task,因为我希望UI保持响应状态.

The problem is: I can't wait for my Task to execute, because I want my UI to remain responsive.

在出现故障的任务上创建任务继续,该任务继续将读取Task.Exception并相应地无法处理:

Create a task continuation on faulted that would read Task.Exception and handle accordingly doesn't work:

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);
}

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

A try / catch in the function also doesn't help (as could be expected).

那么我如何在不等待任务的情况下正确处理任务引发的AggregateExceptions.

So how do I react properly on AggregateExceptions thrown by the task without waiting for it.

推荐答案

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

If you can use .NET 4.5, then I would use the newer async/await, which simplifies the code a lot, and saves you from having to deal with continuations and AggregateExceptions, which just create noise in the code and distract you from focusing on what you are actually trying to accomplish.

它看起来像这样:

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;
}

这篇关于使用Tasks,Handle AggregateException保持UI响应的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆