c# – system.threading.task – 为什么不发生TaskScheduler.Uno
我见过的一个常见问题是管理任务中未处理的异常.它们不会导致崩溃,它们会无声地发生,我甚至无法在任务失败时触发事件!我已经看到用户开出自定义类和处理这个的东西,我的问题是,是否有一种“标准”的微软方式来处理这个问题?
为了举例,我制作了这个简单的控制台应用程序(.NET 4.5.1)来演示这个问题.是否可以修改这些任务以便可以异步执行这些任务,但在遇到未处理的异常时调用“handler”?或者至少崩溃?我认为这就是UnobservedTaskException应该做的事情. using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApplication4 { class Program { static void Main(string[] args) { TaskScheduler.UnobservedTaskException += Handler; AppDomain.CurrentDomain.UnhandledException += Handler; Task.Run(() => { throw new ApplicationException("I'll throw an unhandled exception"); }); Task.Factory.StartNew(() => { throw new ApplicationException("I'll throw an unhandled exception too"); }); System.Threading.Thread.Sleep(2000); Console.WriteLine("I think everything is just peachy!"); System.Threading.Thread.Sleep(10000); } private static void Handler(Object sender,EventArgs e) { Console.WriteLine("I'm so lonely,won't anyone call me?"); } } } 输出: I think everything is just peachy! 期望的输出: I'm so lonely,won't anyone call me? I'm so lonely,won't anyone call me? I think everything is just peachy! 和/或简单地崩溃,即使这将是对异步任务失败的巨大改进! 编辑:根据此MSDN文章http://msdn.microsoft.com/en-us/library/jj160346%28v=vs.110%29.aspx将此添加到app.config,但没有更改: <?xml version="1.0" encoding="utf-8"?> <configuration> <runtime> <ThrowUnobservedTaskExceptions enabled="true"/> </runtime> </configuration> 解决方法
来自评论:
Task对象的本质是它将来会被完成.预计任务的结果或异常将在未来被观察到,很可能在不同的堆栈帧上或甚至在不同的线程上.这就是为什么Framework(或编译器生成的代码,用于异步任务方法)将异常存储在任务中并且不立即抛出它. 您不会在此处观察任务的结果,甚至不会在任何地方存储对任务对象的引用.从本质上讲,你正在做一个即发即弃的电话.编译器警告(告知不会等待任务),但它并没有消除托管的Task对象仍然被创建的事实,并且在它被垃圾收集之前一直闲逛.此时,将触发TaskScheduler.UnobservedTaskException事件.
如果上面的方法是一个糟糕的设计,那么什么是好的?如果立即抛出异常,以下可能如何工作: var task = Task.Run(() => { throw new ApplicationException("I'll throw an unhanded exception"); }); Thread.Sleep(1000); if (task.IsFaulted) Console.WriteLine(task.Exception.InnerException.Message); 它根本不可能. Sleep之后的代码没有机会以其方式处理异常.因此,目前的行为经过深思熟虑并且非常有意义. 如果您仍希望在发生即发即弃任务时立即观察异常,请使用辅助异步void方法: public static class TaskExt { public static async void Observe( this Task @this,bool continueOnCapturedContext = true) { await @this.ConfigureAwait(continueOnCapturedContext); } } static void Main(string[] args) { TaskScheduler.UnobservedTaskException += Handler; AppDomain.CurrentDomain.UnhandledException += Handler; Task.Run(() => { throw new ApplicationException("I'll throw an unhanded exception"); }) .Observe(); Task.Factory.StartNew(() => { throw new ApplicationException("I'll throw an unhanded exception too"); }) .Observe(); System.Threading.Thread.Sleep(2000); Console.WriteLine("I think everything is just peachy!"); System.Threading.Thread.Sleep(10000); } 您还可以使用async void lambda进行即发即弃调用: Action fireAndForget = async () => { try { await Task.Run(...); } catch(e) { /* handle errors */ }; }; fireAndForget(); 我描述了异步void方法in more details here的异常传播行为.
我并不是说这些任务并不意味着可以使用“一劳永逸”的模式.我实际上认为他们非常适合这一点.有一些选项可以用任务来实现这个模式,我在上面展示了一个,另一个是Task.ContinueWith,或者你可以查看this question以获得更多的想法. 另一个问题是我几乎无法想象一个有用的场景,我不想观察任务的完成状态,即使对于像上面这样的点火器也是如此.这样的任务会做什么工作?即使是日志记录,我仍然希望确保成功编写日志文件.此外,如果父进程在任务完成之前结束,该怎么办?根据我的经验,我总是使用像 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |