[英]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.