简体   繁体   English

使用Tasks,Handle AggregateException保持UI响应

[英]Keep UI responsive using Tasks, Handle AggregateException

I have a problem with handling the AggregateException if my WinForms application starts a task to keep responsive while the task is performing. 如果我的WinForms应用程序启动任务以在执行任务时保持响应,我在处理AggregateException时会遇到问题。

The simplified case is as follows. 简化的情况如下。 Suppose my Form has a fairly slow method, for instance: 假设我的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;
}

After pressing a button I want my form to show the result of SlowDivision(3,4). 按下按钮后,我希望我的表单显示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();
}

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: 为了防止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();
        }));
    });
}

So far, so good, but how to handle exceptions, for instance If I want to calculate SlowDivision(3, 0)? 到目前为止,一切都很好,但是如何处理异常,例如,如果我想计算SlowDivision(3,0)?

Normally if a task throws an unhandled exception, it is forwarded to a waiting thread via an AggregateException. 通常,如果任务抛出未处理的异常,则会通过AggregateException将其转发给正在等待的线程。 Numerous examples show the following code: 许多示例显示以下代码:

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

The problem is: I can't wait for my Task to execute, because I want my UI to remain responsive. 问题是:我等不及执行任务,因为我希望UI保持响应状态。

Create a task continuation on faulted that would read Task.Exception and handle accordingly doesn't work: 在出现故障的任务上创建任务继续,该任务继续将读取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);
}

A try / catch in the function also doesn't help (as could be expected). 尝试/捕获功能也无济于事(可以预料)。

So how do I react properly on AggregateExceptions thrown by the task without waiting for it. 因此,如何在不等待任务的情况下正确处理任务引发的AggregateExceptions。

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 AggregateException s, which just create noise in the code and distract you from focusing on what you are actually trying to accomplish. 如果您可以使用.NET 4.5 ,那么我将使用更新的async/await ,这将大大简化代码,并使您不必处理continuation和AggregateException ,后者只会在代码中产生干扰并分散您的注意力关于您实际要完成的工作。

It would look something like this: 它看起来像这样:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM