如何将异常从ContinueWith传播到无限循环任务的调用上下文
本文关键字:任务 无限循环 调用 上下文 传播 异常 ContinueWith | 更新日期: 2023-09-27 18:19:50
我在一个任务中有一个无限循环。在某些情况下,此任务会抛出异常并终止。请考虑以下代码片段。
private async void Button_Click(object sender, RoutedEventArgs e)
{
try
{
int x = await FirstTask();
window.Title = "FirstTask completed with " + x.ToString();
}
catch (ArgumentException ex)
{
textbox.Text = ex.Message;
}
}
public async Task<int> FirstTask()
{
Task<int> secondTask;
int result;
secondTask = SecondTask();
textbox.Text = "Awaiting SecondTask result";
result = await secondTask;
textbox.Text = result;
secondTask.ContinueWith(async (Task t) =>
{
var thirdTask = ThirdTask();
thirdTask.ContinueWith(
async (m) =>
await Task.Run(() =>
{
throw thirdTask.Exception.InnerException;
}),
TaskContinuationOptions.OnlyOnFaulted);
}, TaskContinuationOptions.OnlyOnRanToCompletion);
return 5;
}
public async Task<int> SecondTask()
{
await Task.Delay(1500);
return 8;
}
public async Task ThirdTask()
{
while (true)
{
await Task.Delay(500);
throw new ArgumentException("thirdException");
}
}
我的问题在于无法将ThirdTask引发的异常传播到Button_Click事件。显然,等待它不是一种选择,因为它是一个持续的无限操作(这只是简化为快速失败)。然而,如果只有在ThirdTask失败时才触发"短"任务,那么等待重新抛出异常的"短"命令没有问题。请注意,我对ThirdTask的操作不感兴趣,除非它失败,也就是说,我可以在事件处理程序中等待FirstTask。
实验表明,即使是最简单的例子也不会传播ContinueWith块中的异常。
private async void Button_Click(object sender, RoutedEventArgs e)
{
try
{
Task task = Task.Run(async () => { await Task.Delay(1000); });
task.ContinueWith( (t) => { throw new ArgumentException("test"); }, TaskContinuationOptions.OnlyOnRanToCompletion);
}
catch (ArgumentException ex)
{
textbox.Text = ex.Message;
}
}
那么,考虑到抛出异常的任务有一个无限循环,这使我无法等待它,我如何将异常从ContinueWith传播到调用上下文?
我试图解决的问题有两个方面:首先,我需要初始化一个资源(FirstTask),为了做到这一点,我首先需要获取它(SecondTask),然后用它开始一个进程(ThirdTask),最后,资源的初始化(FirstTask)返回一个指示资源状态的值,该值不依赖于进程(Third Task)。流程(ThirdTask)重复调用另一个任务(在本例中为task.Delay)并对其执行一些工作,但它可能会失败。在这种情况下,它抛出一个需要处理的异常。
第二部分是第二个代码示例的一般情况,说明如何从ContinueWith抛出异常,由调用上下文处理。
考虑到抛出它的任务有一个无限循环,这阻止了我等待它?
这绝不能阻止你等待它。处理它抛出异常的[最简单]方法是专门针对await
it。
你可以简单地实现这样的方法:
public async Task FirstTask()
{
Task<int> secondTask = SecondTask();
textbox.Text = "Awaiting SecondTask result";
textbox.Text = await secondTask;
await ThirdTask();
}
如果点击处理程序需要用第二次操作的结果更新texbox,并且在第三次操作失败时更新UI,那么您不需要将这两次操作都包装在FirstTask
中,并直接从点击处理程序调用它们:
private async void Button_Click(object sender, RoutedEventArgs e)
{
try
{
textbox.Text = "Awaiting SecondTask result";
int x = await SecondTask();
window.Title = "SecondTask completed with " + x.ToString();
await ThirdTask();
}
catch (ArgumentException ex)
{
textbox.Text = ex.Message;
}
}