如何将ContinueWith中的异常传播到无限循环任务的调用上下文

时间:2015-10-30 10:06:13

标签: c# async-await c#-5.0

我在任务中有一个无限循环。在某些情况下,此任务会抛出异常并终止。请考虑以下代码段。

    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)返回指示资源状态的值,它不依赖于进程(ThirdTask)。进程(ThirdTask)重复调用另一个任务(在本例中为Task.Delay)并对其执行一些工作,但它可能会失败。在这种情况下,它会抛出一个需要处理的异常。

第二部分是第二个代码示例的一般情况,关于如何从ContinueWith抛出异常以由调用上下文处理。

1 个答案:

答案 0 :(得分:1)

  

鉴于抛出它的任务有一个无限循环,这阻止我等待它?

这绝不会阻止你等待它。处理它抛出异常的情况的[最简单]方法特别是await它。

您可以简单地实现此方法:

public async Task FirstTask()
{
    Task<int> secondTask = SecondTask();
    textbox.Text = "Awaiting SecondTask result";
    textbox.Text = await secondTask;
    await ThirdTask();
}

如果click处理程序需要同时使用第二个操作的结果更新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;
    }
}