异步等待、异常处理和使用TaskContinuationOptions.OnlyOnFaulted继续
本文关键字:TaskContinuationOptions OnlyOnFaulted 继续 等待 异常处理 异步 | 更新日期: 2023-09-27 18:00:40
我有以下示例控制台应用程序的代码,但从未调用在使用TaskContinuationOptions.OnlyOnFaulted
的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
方法的返回类型为Task
。
通过调用Factory.StartNew
,您实际上创建了一个任务,该任务的方法体只是启动实际任务,但实际任务上没有延续;只有外部多余的任务(不会抛出异常)才有一个。
此外,您的异步函数是async void
。这使得无法添加延续。我将更进一步,说永远不应该生成async void
的函数。它应该始终是async Task
。
如果您更改了语法,那么您的调用语法实际上更简单:
NewTask(index).ContinueWith(HandleException, cts.Token,
TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.Default);
然而,请记住,取消不是免费的;您要么需要将您的令牌传递给其他使用它的异步任务,要么需要自己检查它(或调用.ThrowIfCancellationRequested()
)。
顺便说一句,根据惯例,任何返回Task
或Task<T>
的函数的名称都应以Async
结尾(即应命名为NewTaskAsync
)。
我有以下用于示例控制台应用程序的代码
我想你这样做是为了学习async
和await
。以下是一些提示。
- 不要使用
Task.Factory.StartNew
。大多数时候,不需要在后台线程上执行代码,但如果需要,请使用Task.Run
- 不要使用
ContinueWith
。请改用await
- 遵循基于任务的异步模式
- 理解控制台应用程序是而不是异步应用程序的正常用例。如果你打算最终进入UI应用程序,那么你应该从UI应用程序开始
对于控制台应用程序,只需编写一个Main
,如下所示:
public static void Main(string[] args)
{
MainAsync().Wait();
}
然后把你真正的逻辑放到一个真正的async
方法中:
public static async Task MainAsync()
{
...
}
你可能会发现我的async
简介很有帮助。