C# 使用TaskContinuationOptions.OnlyOnFaulted异步等待、异常处理和继续

C# 使用TaskContinuationOptions.OnlyOnFaulted异步等待、异常处理和继续,c#,asynchronous,task-parallel-library,async-await,C#,Asynchronous,Task Parallel Library,Async Await,对于示例控制台应用程序,我有以下代码,但是使用TaskContinuationOptions在Task continuation中指定的方法 using System; using System.Threading; using System.Threading.Tasks; namespace Sample { class Program { public static void Main(string[] args) {

对于示例控制台应用程序,我有以下代码,但是使用
TaskContinuationOptions在Task continuation中指定的方法

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
方法的返回类型是
任务

。StartNew
,实际上您正在创建一个任务,其方法体只是启动您的实际任务,但您的实际任务没有继续;只有外部的多余任务(不会引发异常)有一个异常

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

如果更改该语法,则调用语法实际上更简单:

NewTask(index).ContinueWith(HandleException, cts.Token, 
    TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.Default);
然而,请记住,取消并不是免费的;您需要将令牌传递给其他使用令牌的异步任务,或者自己检查令牌(或者调用
.ThrowIfCancellationRequested()


另一方面,按照惯例,任何返回
任务
任务
的函数的名称应以
异步
结尾(即,它应命名为
NewTaskAsync
)。

通过调用
工厂。StartNew
,实际上是在创建一个任务,其方法体刚刚启动您的实际任务,但你的实际任务并没有继续下去;只有外部的多余任务(不会引发异常)有一个异常

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

如果更改该语法,则调用语法实际上更简单:

NewTask(index).ContinueWith(HandleException, cts.Token, 
    TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.Default);
然而,请记住,取消并不是免费的;您需要将令牌传递给其他使用令牌的异步任务,或者自己检查令牌(或者调用
.ThrowIfCancellationRequested()

另一方面,按照惯例,任何返回
任务
任务
的函数的名称应以
异步
结尾(即,它应命名为
NewTaskAsync

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

我假设您这样做是为了学习
async
wait
。这里有一些提示

  • 不要使用
    Task.Factory.StartNew
    。大多数情况下,您不需要在后台线程上执行代码,但如果需要,请使用
    Task.Run
  • 不要使用
    ContinueWith
    。改用
    wait
  • 遵循规则
  • 了解控制台应用程序不是异步应用程序的正常用例。如果您计划在UI应用程序中结束,那么您应该从UI应用程序开始
对于控制台应用程序,只需编写一个
Main
,如下所示:

public static void Main(string[] args)
{
  MainAsync().Wait();
}
然后将真实逻辑放入真实的
async
方法中:

public static async Task MainAsync()
{
  ...
}
你会发现我的建议很有帮助

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

我假设您这样做是为了学习
async
wait
。这里有一些提示

  • 不要使用
    Task.Factory.StartNew
    。大多数情况下,您不需要在后台线程上执行代码,但如果需要,请使用
    Task.Run
  • 不要使用
    ContinueWith
    。改用
    wait
  • 遵循规则
  • 了解控制台应用程序不是异步应用程序的正常用例。如果您计划在UI应用程序中结束,那么您应该从UI应用程序开始
对于控制台应用程序,只需编写一个
Main
,如下所示:

public static void Main(string[] args)
{
  MainAsync().Wait();
}
然后将真实逻辑放入真实的
async
方法中:

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

您可能会发现我的帮助。

您正在创建两个任务:一个是
Task NewTask(int index)
的返回值,另一个是调用
Task.Factory.StartNew
的返回值。后者是前者的无用包装。您正在将延续附加到该包装,而不是附加到可能引发异常的实际有用的内部任务。在某些情况下,返回void是合理的,因为您正在创建两个任务:一个是
task NewTask(int index)
,另一个是调用
task.Factory.StartNew
的返回值。后者是前者的无用包装。您正在将延续附加到该包装,而不是附加到可能引发异常的实际有用的内部任务。在某些情况下,返回void是合理的,可以这样更改代码:
task.Factory.StartNew(()=>NewTask(index)、cts.Token、TaskCreationOptions.longlunning、TaskScheduler.Default)。Unwrap().ContinueWith(HandleException,cts.Token,TaskContinuationOptions.OnlyOnFaulted,TaskScheduler.Default);
注意
展开()
,更多详细信息。不,不要这样做,因为这只会使调用代码更复杂。异步函数应该直接调用。@AdamRobinson,“异步函数应该直接调用”-通常是这样,但这实际上取决于您在
async
函数中执行的操作。一个值得注意的情况是,当您需要使用非默认任务调度程序时。另一种可能的情况是,在
async请求时,有一段CPU绑定或阻塞代码<