如何使用 OnlyOnFaulted 选项等待任务?

How to wait on Task with OnlyOnFaulted option?

我有这个简单的 HelloTask WorldTask 控制台程序。当 HelloTask 抛出错误时,我想调用 ExceptionTask 然后通知用户程序结束。

我可以调用 ExceptionTask,但无法等待任务。我收到 TaskCanceledException 或在 ExceptionTask 完成之前打印了用户消息。

如何解决这个问题?有没有办法等待 ExceptionTask 或检查任务是否被取消?

 class Program
    {
        static void Main(string[] args)
        {
            Task helloTask = Task.Run(() => HelloTask());

            var worldTask = helloTask.ContinueWith((prevTask) => WorldTask(), 
                    TaskContinuationOptions.OnlyOnRanToCompletion);

            var exceptionTask = helloTask.ContinueWith((prevTask) => ExceptionTask(), 
                    TaskContinuationOptions.OnlyOnFaulted);

            // When no error: AggregationException is thrown 
            // with message "TaskCanceledException: A task was canceled.".
            // exceptionTask.Wait(); 

            // When error: AggregationException is thrown 
            // with message "TaskCanceledException: A task was canceled."
            // worldTask.Wait();

            // When error: After worldTask is cancelled user message is called, 
            // but that is before exceptionTask is finished !
            //Task.WaitAny(worldTask, exceptionTask); 

            var userMessage = "Finished processing. Press a key to end.";
            Console.WriteLine(userMessage);
            Console.ReadKey();
        }

        static void HelloTask()
        {
            Thread.Sleep(1000);
            // Intentional error.
            var x = 0;
            var y = 1 / x;
            Console.WriteLine("Hello");
        }

        static void WorldTask()
        {
            Thread.Sleep(1000);
            Console.WriteLine("World");
        }

        static void ExceptionTask()
        {
            Thread.Sleep(1000);
            Console.WriteLine("Exception happened!");
        }
}

您可以使用 TaskContinuationOptions.AttachedToParent 然后等待原始任务。

例如:

        Task helloTask = Task.Run(() => HelloTask());

        var worldTask = helloTask.ContinueWith((prevTask) => WorldTask(), 
                TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.AttachedToParent);

        var exceptionTask = helloTask.ContinueWith((prevTask) => ExceptionTask(), 
                TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.AttachedToParent);

        await helloTask;  

根据您是否要等待 worldTask 的需要而有所不同。

然后使用 try catch 语句并在调用它的地方捕获相关的异常(取消或聚合)。

您可以使用 async void 方法作为任务 "completion event" 的处理程序:

Task helloTask = Task.Run(() => Hello());
OnHelloCompletion();

async void OnHelloCompletion()
{
    try
    {
        await helloTask;
        Console.WriteLine("Hello completed successfully");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Hello failed, {ex}");
        Environment.Exit(1);
    }
}

与使用 ContinueWith 方法相比的优点是代码的表现力更强,而且不会创建即发即弃 Task。如果 OnHelloCompletion 方法中有任何导致意外异常的错误,您将立即了解它,因为 async void 方法中的异常会导致应用程序立即崩溃。就像 UI 应用程序的事件处理程序中抛出的未处理异常一样。

How to solve this issue ? Is there a way to wait on ExceptionTask or check if a task was cancelled ?

我强烈建议将 ContinueWith 的所有实例替换为 await:

static async Task Main(string[] args)
{
  try
  {
    await Task.Run(() => HelloTask());
    WorldTask();
  }
  catch
  {
    ExceptionTask();
  }

  var userMessage = "Finished processing. Press a key to end.";
  Console.WriteLine(userMessage);
  Console.ReadKey();
}