Async-Await,使用TaskContinuationOptions.OnlyOnFaulted进行异常处理和继续

时间:2014-04-28 21:37:34

标签: c# asynchronous task-parallel-library async-await

我有以下代码用于示例控制台应用程序,但使用TaskContinuationOptions.OnlyOnFaulted的任务继续中指定的方法永远不会被调用。

using System;
using System.Threading;
using System.Threading.Tasks;

namespace Sample
{
    class Program
    {
        public static void Main(string[] args)
        {
            int index = 0;
            var cts = new CancellationTokenSource();

            Task.Factory.StartNew(() => NewTask(index), cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default)
                .ContinueWith(HandleException, cts.Token, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.Default);

            // Some code
            // ...

            Console.ReadLine();
        }

        private static async Task NewTask(int index)
        {
            Console.WriteLine(index);
            await Wait();
        }

        private static async Task Wait()
        {
            await Task.Delay(500);
            throw new Exception("Testing 123");
        }

        private static void HandleException(Task task)
        {
            if (task != null && task.Exception != null)
            {
                var exceptions = task.Exception.Flatten().InnerExceptions;
                if (exceptions != null)
                    foreach (var exception in exceptions)
                        Console.WriteLine(exception.Message);
            }
        }
    }
}

这里,当从Wait()方法抛出异常,而不是调用HandleException(...)时,程序崩溃或调试器都会显示未处理的异常对话框。

已编辑: NewTask方法的返回类型为Task

2 个答案:

答案 0 :(得分:4)

通过调用Factory.StartNew,您实际上正在创建一个任务,其方法正文只是启动您的实际任务,但您的实际任务没有继续;只有外部的,多余的任务(不会抛出异常)才有一个。

此外,您的异步功能是async void。这使得无法添加延续。我将更进一步说,你永远不应该创建一个async void 的函数。它应始终为async Task

如果你改变语法,你的调用语法实际上更简单:

NewTask(index).ContinueWith(HandleException, cts.Token, 
    TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.Default);

但是,请记住,取消不是免费获得的;您需要将令牌传递给其他使用它的异步任务,或者您需要自己检查它(或致电.ThrowIfCancellationRequested())。

顺便说一句,按照惯例,任何返回TaskTask<T>的函数的名称都应以Async结尾(即它应该命名为NewTaskAsync)。< / p>

答案 1 :(得分:3)

  

我有以下代码用于示例控制台应用程序

我认为你这样做是为了学习asyncawait。这里有一些提示。

  • 不要使用Task.Factory.StartNew。大多数情况下,您需要在后台线程上执行代码,但如果您这样做,请使用Task.Run
  • 不要使用ContinueWith。请改用await
  • 关注Task-based Asynchronous Pattern
  • 了解控制台应用程序是异步应用程序的正常用例。如果您计划在UI应用程序中结束,那么您应该从UI应用程序开始。

对于控制台应用,只需写一个Main,如下所示:

public static void Main(string[] args)
{
  MainAsync().Wait();
}

然后将您的真实逻辑转换为真正的async方法:

public static async Task MainAsync()
{
  ...
}

您可能会发现我的async intro有帮助。