【问题标题】:How can I monitor the Task queues in the .NET TaskSchedulers (across AppDomain)如何监视 .NET TaskScheduler 中的任务队列(跨 AppDomain)
【发布时间】:2015-04-09 22:03:08
【问题描述】:

作为一名开发人员,我想监控 TaskScheduler 中任务队列中工作的大小(和进度),以便我可以评估在生产负载下经历的减速是否是由于计划任务的量级或延迟造成的.

通常我会简单地附加调试器并检查任务大小,但是:

  1. 该应用程序在 Mono 下运行并处于生产状态,因此我无法使用 Visual Studio 附加到它
  2. 我想提供对此数据的分析输出,作为服务健康监测的监测输入

我已经通过文档找到了

  • TaskScheduler.GetScheduledTasks,它将此信息传递给调试器。它受到保护(我可以规避),但似乎也需要一些关于我无法兑现的冻结线程的保证。但是,我愿意使用不一致的数据。
  • how to get a list of running tasks in .net 4.0。它侧重于 running 任务,这对我来说不是那么有趣。我对积压的大小以及工作是否正在进行中感兴趣。

我愿意:

  1. 使用为其他目的(例如调试器)设计的代码
  2. 接受我得到不一致的数据(这是为了统计和分析)

不想想做的事情是:

  1. 为每个创建的任务添加跟踪。
    • 部分代码是第 3 方的,无法重构
    • 很多代码,将其注入其中比不知道它的进展情况更糟糕。
  2. 使用自定义任务调度器
    • 部分代码是第 3 方的,无法重构
    • 一些代码使用了TaskScheduler,它设置了线程优先级和最大并发。考虑到监控方面会很糟糕。

【问题讨论】:

    标签: .net task-parallel-library task monitoring


    【解决方案1】:

    我能够使用反射和 GetScheduledTasksForDebugger 检索信息:

    public static class ScheduledTaskAccess
    {
        public static Task[] GetScheduledTasksForDebugger(this TaskScheduler ts)
        {
            var mi = ts.GetType().GetMethod("GetScheduledTasksForDebugger", BindingFlags.NonPublic | BindingFlags.Instance);
            if (mi == null)
                return null;
            return (Task[])mi.Invoke(ts, new object[0]);
        }
        public static TaskScheduler[] GetTaskSchedulersForDebugger()
        {
            var mi = typeof(TaskScheduler).GetMethod("GetTaskSchedulersForDebugger", BindingFlags.NonPublic | BindingFlags.Static);
            if (mi == null)
                return null;
            return (TaskScheduler[])mi.Invoke(null, new object[0]);
        }
    }
    

    使用自定义的 IEqualityComparer 我能够维持他们排队的时间。非常有用的元信息:)

    现在我只希望有一些关于可以访问的任务的统计信息,CPU 时间花费,空闲时间,任务切换计数,...

    【讨论】:

    • 在生产环境中运行上述代码一段时间后,我现在知道它有时会导致单声道崩溃:(
    • 即使微软完全不鼓励使用“GetTaskSchedulersForDebugger()”,干得好!我测试了,它有效!我认为这是完全不可能的。
    【解决方案2】:

    很遗憾,TaskScheduler.GetScheduledTasks 仅针对调试器而设计,无法直接获取计划任务。

    但是 MSDN 说 (source):

    实现自定义调试器的开发人员不应直接调用此方法,而应使用内部包装方法GetScheduledTasksForDebuggerinternal Task[] GetScheduledTasksForDebugger()。这个包装方法返回一个任务数组而不是一个可枚举的。要检索活动调度程序列表,请使用内部方法internal static TaskScheduler[] GetTaskSchedulersForDebugger()。此静态方法返回所有活动 TaskScheduler 实例的数组。然后,您可以在每个调度程序实例上使用GetScheduledTasksForDebugger 来检索其计划任务列表。

    那么为什么不使用反射呢?这是实现您想要的安全且简单的方法,因为GetScheduledTasksForDebugger 将在未来存在,并且它是为调试而设计的,并且可能用于分析。

    【讨论】:

      最近更新 更多