简体   繁体   English

使用TaskContinuationOptions.OnlyOnFaulted进行异步等待,异常处理和继续

[英]Async-Await, exception handling and continuation using TaskContinuationOptions.OnlyOnFaulted

I have the following code for sample console application, but the method specified in Task continuation using TaskContinuationOptions.OnlyOnFaulted never gets called. 我为示例控制台应用程序提供了以下代码,但从未调用使用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);
            }
        }
    }
}

Here, when an exception is thrown from Wait() method, instead of HandleException(...) being called, either program crashes or debugger shows an unhandled exception dialog box. 在这里,当从Wait()方法引发异常时,而不是调用HandleException(...) ,程序崩溃或调试器将显示未处理的异常对话框。

EDITED: Return type of NewTask method is Task . 编辑: NewTask方法的返回类型是Task

By calling Factory.StartNew , you're actually creating a task whose method body just initiates your actual task , but your actual task has no continuation on it; 通过调用Factory.StartNew ,您实际上是在创建一个任务该任务的方法主体只是初始化您的实际任务 ,但是您的实际任务没有延续性; only the outer, superfluous task (which won't throw an exception) has one. 只有外部多余的任务(不会抛出异常)才有一个。

Additionally, your async function is async void . 另外,您的异步函数是async void This makes it impossible to add a continuation. 这使得不可能添加延续。 I'm going to go one step further and say that you should never, ever, make a function that's async void . 我要更进一步,说您永远都不要做一个async void的函数 It should always be async Task . 它应该始终是async Task

If you make that syntax change, your calling syntax is actually simpler: 如果您更改了语法,则调用语法实际上会更简单:

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

However, bear in mind that cancellation is not something that you get for free; 但是,请记住,取消并不是您免费获得的东西。 you either need to be passing your token along to other asynchronous tasks that make use of it or you need to be checking it youself (or call .ThrowIfCancellationRequested() ). 您要么需要将令牌传递给其他使用它的异步任务,要么需要自己进行检查(或调用.ThrowIfCancellationRequested() )。

As an aside, by convention any function which returns a Task or Task<T> should have its name end with Async (ie it should be named NewTaskAsync ). NewTaskAsync ,按照约定,任何返回TaskTask<T>函数的名称都应以Async结尾(即,应将其命名为NewTaskAsync )。

I have the following code for sample console application 我有以下示例控制台应用程序代码

I assume you're doing this to learn async and await . 我认为您正在这样做是为了学习asyncawait Here's some tips. 这里有一些技巧。

  • Don't use Task.Factory.StartNew . 不要使用Task.Factory.StartNew Most of the time you don't need to execute code on a background thread, but if you do, then use Task.Run . 大多数时候,您不需要在后台线程上执行代码,但是如果这样做,请使用Task.Run
  • Don't use ContinueWith . 不要使用ContinueWith Use await instead. 请改用await
  • Follow the Task-based Asynchronous Pattern . 遵循基于任务的异步模式
  • Understand that Console applications are not a normal use case for asynchronous applications. 了解控制台应用程序不是异步应用程序的正常用例。 If you're planning to end up in a UI application, then you should start in a UI application. 如果您打算最终在UI应用程序中使用,则应在UI应用程序中启动。

For Console apps, just write a Main like this: 对于控制台应用程序,只需编写如下的Main

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

And then put your real logic into a real async method: 然后将真正的逻辑放入真正的async方法中:

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

You may find my async intro helpful. 您可能会发现我的async介绍很有帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM