[英]Keep UI responsive using Tasks, Handle 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;
}
按下按钮后,我希望我的表单显示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);
}
尝试/捕获功能也无济于事(可以预料)。
因此,如何在不等待任务的情况下正确处理任务引发的AggregateExceptions。
如果您可以使用.NET 4.5
,那么我将使用更新的async/await
,这将大大简化代码,并使您不必处理continuation和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;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.