c# – 我如何允许任务异常传播回UI线程?
在TPL中,如果
Task抛出异常,则该异常将被捕获并存储在
Task.Exception中,然后遵循
observed exceptions的所有规则.如果从未被观察到,则最终会在终结器线程上重新抛出并导致该进程崩溃.
有没有办法阻止该任务捕获该异常,只是让它传播? 我感兴趣的任务已经在UI线程上运行(由TaskScheduler.FromCurrentSynchronizationContext提供),我想要将异常转义为可以由我现有的Application.ThreadException处理程序处理. 我基本上希望任务中的未处理的异常在按钮单击处理程序中表现为未处理的异常:立即在UI线程上传播,并由ThreadException处理. 解决方法
Ok Joe …如所承诺的,下面是如何通过自定义TaskScheduler子类来一般性地解决这个问题.我已经测试了这个实现,它的作用就像一个魅力.不要忘记,如果你想看到Application.ThreadException实际上启动,你不能附加调试器!
自定义TaskScheduler 这个自定义TaskScheduler实现被绑定到一个特定的SynchronizationContext在“诞生”,并将采取每个传入的任务,它需要执行,链接一个继续,只会触发逻辑任务故障,当它触发时,它返回到SynchronizationContext,它将从任务中抛出异常. public sealed class SynchronizationContextFaultPropagatingTaskScheduler : TaskScheduler { #region Fields private SynchronizationContext synchronizationContext; private ConcurrentQueue<Task> taskQueue = new ConcurrentQueue<Task>(); #endregion #region Constructors public SynchronizationContextFaultPropagatingTaskScheduler() : this(SynchronizationContext.Current) { } public SynchronizationContextFaultPropagatingTaskScheduler(SynchronizationContext synchronizationContext) { this.synchronizationContext = synchronizationContext; } #endregion #region Base class overrides protected override void QueueTask(Task task) { // Add a continuation to the task that will only execute if faulted and then post the exception back to the synchronization context task.ContinueWith(antecedent => { this.synchronizationContext.Post(sendState => { throw (Exception)sendState; },antecedent.Exception); },TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.ExecuteSynchronously); // Enqueue this task this.taskQueue.Enqueue(task); // Make sure we're processing all queued tasks this.EnsureTasksAreBeingExecuted(); } protected override bool TryExecuteTaskInline(Task task,bool taskWasPreviouslyQueued) { // Excercise for the reader return false; } protected override IEnumerable<Task> GetScheduledTasks() { return this.taskQueue.ToArray(); } #endregion #region Helper methods private void EnsureTasksAreBeingExecuted() { // Check if there's actually any tasks left at this point as it may have already been picked up by a previously executing thread pool thread (avoids queueing something up to the thread pool that will do nothing) if(this.taskQueue.Count > 0) { ThreadPool.UnsafeQueueUserWorkItem(_ => { Task nextTask; // This thread pool thread will be used to drain the queue for as long as there are tasks in it while(this.taskQueue.TryDequeue(out nextTask)) { base.TryExecuteTask(nextTask); } },null); } } #endregion } 有关此实施的注释/免责声明: >如果使用无参数的构造函数,它将会拾取当前的SynchronizationContext …,所以如果你只是在一个WinForms线程(主窗体构造函数,无论什么)构造它,它将自动工作.奖金,我也有一个构造函数,你可以在其中明确地传入你从别的地方得到的SynchronizationContext. 好的,现在你有几个选项来使用这个TaskScheduler: 预配置TaskFactory实例 此方法允许您一次设置TaskFactory,然后从该工厂实例开始的任何任务将使用自定义TaskScheduler.这基本上看起来像这样: 在应用程序启动时 private static readonly TaskFactory MyTaskFactory = new TaskFactory(new SynchronizationContextFaultPropagatingTaskScheduler()); 整个代码 MyTaskFactory.StartNew(_ => { // ... task impl here ... }); 显式TaskScheduler每次呼叫 另一种方法是创建自定义TaskScheduler的实例,然后在每次启动任务时将其传递到默认的TaskFactory上的StartNew中. 在应用程序启动时 private static readonly SynchronizationContextFaultPropagatingTaskScheduler MyFaultPropagatingTaskScheduler = new SynchronizationContextFaultPropagatingTaskScheduler(); 整个代码 Task.Factory.StartNew(_ => { // ... task impl here ... },CancellationToken.None // your specific cancellationtoken here (if any) TaskCreationOptions.None,// your proper options here MyFaultPropagatingTaskScheduler); (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |