如何允许任务异常传播回 UI 线程?

2024-03-31

在 TPL 中,如果异常抛出Task http://msdn.microsoft.com/en-us/library/system.threading.tasks.task.aspx,该异常被捕获并存储在任务异常 http://msdn.microsoft.com/en-us/library/system.threading.tasks.task.exception.aspx,然后遵循所有规则观察到的异常 http://msdn.microsoft.com/en-us/library/dd997415.aspx。如果从未观察到它,它最终会在终结器线程上重新抛出并使进程崩溃。

有没有办法防止任务捕获该异常,而只是让它传播?

我感兴趣的任务已经在 UI 线程上运行(由TaskScheduler.FromCurrentSynchronizationContext http://msdn.microsoft.com/en-us/library/system.threading.tasks.taskscheduler.fromcurrentsynchronizationcontext.aspx),并且我希望异常能够转义,以便可以由我现有的处理应用程序线程异常 http://msdn.microsoft.com/en-us/library/system.windows.forms.application.threadexception.aspx处理程序。

我基本上希望任务中未处理的异常表现得像按钮单击处理程序中未处理的异常:立即在 UI 线程上传播,并由 ThreadException 处理。


好吧,乔...正如所承诺的,以下是如何使用自定义解决此问题的方法TaskScheduler子类。我已经测试了这个实现,它的效果非常好。别忘了如果您想查看,则无法附加调试器Application.ThreadException真的要火了!!!

自定义任务调度程序

这个自定义的 TaskScheduler 实现与特定的SynchronizationContext在“出生”时,将采取每个传入Task它需要执行,将一个 Continuation 链接到它上面,只有在逻辑满足时才会触发Task故障,当它着火时,它Post返回到 SynchronizationContext,它将抛出异常Task那个错了。

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 的实现,因此这个实现总是只是将Task有待研究。我将其作为练习留给读者。这并不难,只是......没有必要演示您所要求的功能。
  • 我正在使用一种简单/原始的方法来调度/执行利用线程池的任务。肯定有更丰富的实现,但是这个实现的重点只是将异常封送回“应用程序”线程

好的,现在您有几个使用此 TaskScheduler 的选项:

预配置 TaskFactory 实例

这种方法允许您设置TaskFactory一次之后,您从该工厂实例开始的任何任务都将使用自定义TaskScheduler。基本上看起来像这样:

在应用程序启动时

private static readonly TaskFactory MyTaskFactory = new TaskFactory(new SynchronizationContextFaultPropagatingTaskScheduler());

整个代码

MyTaskFactory.StartNew(_ =>
{
    // ... task impl here ...
});

每次调用的显式任务调度程序

另一种方法是只创建自定义的实例TaskScheduler然后将其传递到StartNew在默认情况下TaskFactory每次你开始一项任务时。

在应用程序启动时

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);
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何允许任务异常传播回 UI 线程? 的相关文章

  • 将复选框添加到 UniformGrid

    我正在尝试将复选框动态添加到 wpf 中的统一网格中 但看起来网格没有为它们分配足够的空间 所以它们都有点互相重叠 这就是我将它们添加到后面的代码中的方法 foreach string folder in subfolders PathCh
  • 无法使用已与其底层 RCW 分离的 COM 对象。在 oledb 中

    我收到此错误 但我不知道我做错了什么 下面的代码在backrgroundworker中 将异常详细信息复制到剪贴板 System Runtime InteropServices InvalidComObjectException 未处理 通
  • C# 和 Javascript SHA256 哈希的代码示例

    我有一个在服务器端运行的 C 算法 它对 Base64 编码的字符串进行哈希处理 byte salt Convert FromBase64String serverSalt Step 1 SHA256Managed sha256 new S
  • 获取按下的按钮的返回值

    我有一个在特定事件中弹出的表单 它从数组中提取按钮并将标签值设置为特定值 因此 如果您要按下或单击此按钮 该函数应返回标签值 我怎样才能做到这一点 我如何知道点击了哪个按钮 此时代码返回 DialogResult 但我想从函数返回 Tag
  • pthread_cond_timedwait() 和 pthread_cond_broadcast() 解释

    因此 我在堆栈溢出和其他资源上进行了大量搜索 但我无法理解有关上述函数的一些内容 具体来说 1 当pthread cond timedwait 因为定时器值用完而返回时 它如何自动重新获取互斥锁 互斥锁可能被锁定在其他地方 例如 在生产者
  • 未解决的包含:“cocos2d.h” - Cocos2dx

    当我在 Eclipse 中导入 cocos2dx android 项目时 我的头文件上收到此警告 Unresolved inclusion cocos2d h 为什么是这样 它实际上困扰着我 该项目可以正确编译并运行 但我希望这种情况消失
  • 当 contains() 工作正常时,xpath 函数ends-with() 工作时出现问题

    我正在尝试获取具有以特定 id 结尾的属性的标签 like span 我想获取 id 以 国家 地区 结尾的跨度我尝试以下xpath span ends with id Country 但我得到以下异常 需要命名空间管理器或 XsltCon
  • 获取没有非标准端口的原始 url (C#)

    第一个问题 环境 MVC C AppHarbor Problem 我正在调用 openid 提供商 并根据域生成绝对回调 url 在我的本地机器上 如果我点击的话 效果很好http localhost 12345 login Request
  • Cython 和类的构造函数

    我对 Cython 使用默认构造函数有疑问 我的 C 类 Node 如下 Node h class Node public Node std cerr lt lt calling no arg constructor lt lt std e
  • 使用 System.Text.Json 即时格式化 JSON 流

    我有一个未缩进的 Json 字符串 例如 hash 123 id 456 我想缩进字符串并将其序列化为 JSON 文件 天真地 我可以使用缩进字符串Newtonsoft如下 using Newtonsoft Json Linq JToken
  • 如何将图像路径保存到Live Tile的WP8本地文件夹

    我正在更新我的 Windows Phone 应用程序以使用新的 WP8 文件存储 API 本地文件夹 而不是 WP7 API 隔离存储文件 旧的工作方法 这是我如何成功地将图像保存到 共享 ShellContent文件夹使用隔离存储文件方法
  • vector 超出范围后不清除内存

    我遇到了以下问题 我不确定我是否错了或者它是一个非常奇怪的错误 我填充了一个巨大的字符串数组 并希望在某个点将其清除 这是一个最小的例子 include
  • 在数据库中搜索时忽略空文本框

    此代码能够搜索数据并将其加载到DataGridView基于搜索表单文本框中提供的值 如果我将任何文本框留空 则不会有搜索结果 因为 SQL 查询是用 AND 组合的 如何在搜索 从 SQL 查询或 C 代码 时忽略空文本框 private
  • 如何衡量两个字符串之间的相似度? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 给定两个字符串text1 and text2 public SOMEUSABLERETURNTYPE Compare string t
  • Qt表格小部件,删除行的按钮

    我有一个 QTableWidget 对于所有行 我将一列的 setCellWidget 设置为按钮 我想将此按钮连接到删除该行的函数 我尝试了这段代码 它不起作用 因为如果我只是单击按钮 我不会将当前行设置为按钮的行 ui gt table
  • C++ fmt 库,仅使用格式说明符格式化单个参数

    使用 C fmt 库 并给定一个裸格式说明符 有没有办法使用它来格式化单个参数 example std string str magic format 2f 1 23 current method template
  • 需要哪个版本的 Visual C++ 运行时库?

    microsoft 的最新 vcredist 2010 版 是否包含以前的版本 2008 SP1 和 2005 SP1 还是我需要安装全部 3 个版本 谢谢 你需要所有这些
  • Process.Start 阻塞

    我正在调用 Process Start 但它会阻止当前线程 pInfo new ProcessStartInfo C Windows notepad exe Start process mProcess new Process mProce
  • 限制C#中的并行线程数

    我正在编写一个 C 程序来生成并通过 FTP 上传 50 万个文件 我想并行处理4个文件 因为机器有4个核心 文件生成需要更长的时间 是否可以将以下 Powershell 示例转换为 C 或者是否有更好的框架 例如 C 中的 Actor 框
  • 恢复上传文件控制

    我确实阅读了以下帖子 C 暂停 恢复上传 https stackoverflow com questions 1048330 pause resume upload in c 使用 HTTP 恢复上传 https stackoverflow

随机推荐