我有这个简单的HelloTask WorldTask控制台程序。当HelloTask抛出错误时,我想调用ExceptionTask,然后通知用户该程序结束。
我可以调用ExceptionTask,但是我无法等待Task。我收到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
方法作为任务“完成事件”的处理程序:
async void
相比于使用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
方法内部的异常会导致应用程序立即崩溃。就像UI应用程序的事件处理程序中引发的未处理异常一样。