处理异步方法的同步部分中的exception
我正在处理我开始的任务可能抛出的情况,同时仍然在初始线程上同步执行。 出于说明目的,这样的事情:
static async Task TestAsync() { var random = new Random(Environment.TickCount).Next(); if (random % 2 != 0) throw new ApplicationException("1st"); await Task.Delay(2000); Console.WriteLine("after await Task.Delay"); throw new ApplicationException("2nd"); }
从调用代码,我希望能够捕获任何exception,可能从同步部分抛出(即, await Task.Delay()
)。 这是我目前正在做的事情:
static void Main(string[] args) { try { var task = TestAsync(); if (task.IsFaulted) task.GetAwaiter().GetResult(); Console.WriteLine("TestAsync continues asynchronously..."); } catch (Exception e) { Console.WriteLine("Error: " + e.ToString()); } Console.WriteLine("Press Enter to exit..."); Console.ReadLine(); }
虽然它看起来有点满口,但是这个Task
没有Result
。
我也尝试过task.Wait()
而不是task.GetAwaiter().GetResult()
。 这总是给我AggregateException
,我必须解开(而不是直接预期ApplicationException
)。
还有其他选择吗?
[已编辑]要解决这些问题:我这样做,因为如果任务立即失败,我不想将其添加到我维护的待处理任务列表中。 任务本身对这样的列表一无所知(并且它没有必要)。 我仍然想记录exception,并让用户知道它。 我也可以throw task.Exception
,但这不会给ExceptionDispatchInfo
捕获的exception堆栈帧。
[更新]灵感来自其他答案和评论:如果我完全控制TestAsync
并且我不想引入新的类成员,我也可以做类似下面的事情。 validation参数时可能会派上用场:
static Task TestAsync(int delay) { if (delay < 0) throw new ArgumentOutOfRangeException("delay"); Func asyncPart = async () => { Console.WriteLine("await Task.Delay"); await Task.Delay(delay); throw new ApplicationException("2nd"); }; return asyncPart(); }
我将它分成两部分,而不是依赖task.GetAwaiter().GetResult()
来工作。 我担心维护TestAsync
可能会在将来无意中破坏它。
这就是我写它的方式。 这应该保留你已经拥有的行为,但我发现它发生的更明显:
static Task Test() { var random = new Random(Environment.TickCount).Next(); if (random % 2 != 0) throw new ApplicationException("1st"); return TestAsync(); } static async Task TestAsync() { await Task.Delay(2000); Console.WriteLine("after await Task.Delay"); throw new ApplicationException("2nd"); } static void Main(string[] args) { try { Test(); Console.WriteLine("TestAsync continues asynchronously..."); } catch (Exception e) { Console.WriteLine("Error: " + e.ToString()); } Console.WriteLine("Press Enter to exit..."); Console.ReadLine(); }
一般而言,exception不应用于应用程序中的常规error handling。 抛出“exception”情况下的例外情况,因为出现意外情况并且需要进行硬停止,程序无法继续。
当然,我不确切知道你的用例是什么,但每当我使用异步任务时,意外失败的部分通常也应该是异步的部分(例如连接到数据库)。
无论如何,我将如何将你的TestAsync
方法放入它自己的类中。 然后,您可以使用方法(或属性) bool TestAsync.IsValid
来确定任务是否已准备好并应排队等待执行; 然后,如果答案为真,您可以运行异步任务: TestAsync.RunAsync()
。