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.
>我没有提供TryExecuteTaskInline的实现,所以这个实现将只是排队要处理的任务.我把这作为读者的练习.这不难,只是…没有必要展示你要求的功能.
>我使用简单/原始的方法来调度/执行利用ThreadPool的任务.肯定有更丰富的实现,但是这个实现的重点只是将异常封装到“应用程序”线程

好的,现在你有几个选项来使用这个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);

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


原文地址:http://msdn.microsoft.com/en-us/magazine/cc163791.aspx 原文发布日期: 9/19/2005 原文已经被 Microsoft 删除了,收集过程中发现很多文章图都不全,那是因为原文的图都不全,所以特收集完整全文。 目录 前言 CLR启动程序
前言 随着近些年微服务的流行,有越来越多的开发者和团队所采纳和使用,它的确提供了很多的优势也解决了很多的问题,但是我们也知道也并不是银弹,提供优势的同时它也给我们的开发人员和团队也带来了很多的挑战。 为了迎接或者采用这些新技术,开发团队需要更加注重一些流程或工具的使用,这样才能更好的适应这些新技术所
最近因为比较忙,好久没有写博客了,这篇主要给大家分享一下PLINQ中的分区。上一篇介绍了并行编程,这边详细介绍一下并行编程中的分区和自定义分区。 先做个假设,假设我们有一个200Mb的文本文件需要读取,怎么样才能做到最优的速度呢?对,很显然就是拆分,把文本文件拆分成很多个小文件,充分利用我们计算机中
在多核CPU在今天和不久的将来,计算机将拥有更多的内核,Microsoft为了利用这个硬件特性,于是在Visual Studio 2010 和 .NET Framework 4的发布及以上版本中,添加了并行编程这个新特性,我想它以后势必会改变我们的开发方式。 在以前或者说现在,我们在并行开发的时候可
c语言输入成绩怎么判断等级
字符型数据在内存中的存储形式是什么
c语言怎么求字符串的长度并输出
c语言函数的三种调用方式是什么
c语言中保留两位小数怎么表示
double的输入格式符是什么
长整型输出格式是什么
C语言中文件包含的命令关键字是什么
c程序如何编写x的y次方
c语言开根号代码是什么
c语言怎么进行字符串比较
c语言怎么进行强制类型转换
c语言运算符的优先级顺序是什么
c++用什么软件编程
中序遍历是怎么遍历的
h文件和c文件的关系是什么