如何将异常从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抛出异常,由调用上下文处理。

如何将异常从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;
    }
}