我有以下代码用于示例控制台应用程序,但使用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
。
答案 0 :(得分:4)
通过调用Factory.StartNew
,您实际上正在创建一个任务,其方法正文只是启动您的实际任务,但您的实际任务没有继续;只有外部的,多余的任务(不会抛出异常)才有一个。
此外,您的异步功能是async void
。这使得无法添加延续。我将更进一步说,你永远不应该创建一个async void
的函数。它应始终为async Task
。
如果你改变语法,你的调用语法实际上更简单:
NewTask(index).ContinueWith(HandleException, cts.Token,
TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.Default);
但是,请记住,取消不是免费获得的;您需要将令牌传递给其他使用它的异步任务,或者您需要自己检查它(或致电.ThrowIfCancellationRequested()
)。
顺便说一句,按照惯例,任何返回Task
或Task<T>
的函数的名称都应以Async
结尾(即它应该命名为NewTaskAsync
)。< / p>
答案 1 :(得分:3)
我有以下代码用于示例控制台应用程序
我认为你这样做是为了学习async
和await
。这里有一些提示。
Task.Factory.StartNew
。大多数情况下,您不需要在后台线程上执行代码,但如果您这样做,请使用Task.Run
。ContinueWith
。请改用await
。对于控制台应用,只需写一个Main
,如下所示:
public static void Main(string[] args)
{
MainAsync().Wait();
}
然后将您的真实逻辑转换为真正的async
方法:
public static async Task MainAsync()
{
...
}
您可能会发现我的async
intro有帮助。