如何监视 .NET TaskScheduler 中的任务队列(跨 AppDomain)

How can I monitor the Task queues in the .NET TaskSchedulers (across AppDomain)

作为一名开发人员,我想监控 TaskScheduler 中任务队列中工作的大小(和进度),以便我可以评估生产负载下经历的减速是否是由于规模或停滞造成的已安排任务。

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

  1. 该应用程序是 运行 在单声道和生产中,所以我无法使用 visual studio
  2. 附加到它
  3. 我想提供对此数据的分析输出作为服务健康监控的监控输入

我浏览了文档并找到了

我愿意:

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

不想做的事情是:

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

不幸的是,TaskScheduler.GetScheduledTasks 仅供调试器使用,无法直接获取计划任务。

但是 MSDN 说 (source):

Developers who are implementing custom debuggers shouldn't call this method directly, but should use the internal wrapper method GetScheduledTasksForDebugger instead: internal Task[] GetScheduledTasksForDebugger(). This wrapper method returns an array of tasks instead of an enumerable. To retrieve a list of active schedulers, use the internal method internal static TaskScheduler[] GetTaskSchedulersForDebugger(). This static method returns an array of all active TaskScheduler instances. You can then use GetScheduledTasksForDebugger on each scheduler instance to retrieve its list of scheduled tasks.

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

我能够使用反射和 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-花费的时间,空闲时间,任务切换次数,...