我正在尝试获取所有当前正在运行的任务的列表。.net 4.0 tasks api 是否提供这样的功能?或者唯一的选择是明确地将任务存储在单独的集合中?
5 回答
我想你需要TaskScheduler.GetScheduledTasks方法,但是:
- 它受到保护
- MSDN 说它应该只用于调试
- 据我所知,此方法仅由 ThreadPoolTaskScheduler 实现,SynchronizationContextTaskScheduler 始终返回 null
所以我认为你应该尝试实现自己的 TaskScheduler来实现你的目标。
创建任务时,默认情况下该任务被安排在线程池线程上运行。因此,您可以使用ThreadPool.GetMaxThreads和ThreadPool.GetAvailableThreads方法获取正在运行的任务数。
private static int GetWorkingThreads() {
int maxThreads;
int completionPortThreads;
ThreadPool.GetMaxThreads(out maxThreads, out completionPortThreads);
int availableThreads;
ThreadPool.GetAvailableThreads(out availableThreads, out completionPortThreads);
return maxThreads - availableThreads;
}
为什么要查找正在运行的任务列表?除了调试之外,您不必使用此信息。在任何情况下,计划执行的任务列表与实际执行的任务之间都存在差异。
正如 Rusted 所写,您可以从 TaskScheduler.GetScheduledTasks 方法中获取计划任务的数量。该方法是抽象的,因此它必须由所有 TaskScheduler 实现。
实际执行的任务数量取决于 TaskScheduler 的实现。默认任务调度程序使用线程池,在这种情况下,您应该检查ThreadPool.GetAvailableThreads和ThreadPool.GetMaxThreads来估计正在执行的任务数。
即使您使用默认的 TaskScheduler,也没有实际的正在运行的任务列表。调度器本质上是把一个任务分配给一个 ThreadPool 并将实际执行留给池(实际上,它调用了 Task.ExecuteEntry 私有方法)。它不需要保留正在运行的任务列表。
如果您希望运行任务信息用于调试目的,您可以利用TPL 中的 Windows 事件的事件跟踪。不幸的是,没有记录 Task Started 和 Task Finished 事件。我在使用 dotPeek 浏览 Task.ExecuteEntry 的定义时发现了它们。
我找到了一篇探索 TPL 事件的文章,但我不确定这是否值得。除非你自己写调试器,否则似乎太麻烦了。
如果只需要获取正在运行的任务列表,也许您应该编写自己的 TaskScheduler 并覆盖 TryExecute 和 TryExecuteInternal 以拦截每个任务的执行并将每个任务放在一个列表中。不过,这可能会变得昂贵,并且您必须定期进行一些清理以从列表中删除已完成的任务,而无需使用延续(最终会出现在列表中)。
MSDN 上有一篇很好的文章http://msdn.microsoft.com/en-us/library/ms997649.aspx 你需要的一切都描述得很好。
编辑:也许这会有所帮助:
using System;
using System.Diagnostics;
class MainClass
{
public static void Main()
{
Process[] allProcs = Process.GetProcesses();
foreach(Process proc in allProcs)
{
ProcessThreadCollection myThreads = proc.Threads;
Console.WriteLine("process: {0}, id: {1}", proc.ProcessName, proc.Id);
foreach(ProcessThread pt in myThreads)
{
Console.WriteLine(" thread: {0}", pt.Id);
Console.WriteLine(" started: {0}", pt.StartTime.ToString());
Console.WriteLine(" CPU time: {0}", pt.TotalProcessorTime);
Console.WriteLine(" priority: {0}", pt.BasePriority);
Console.WriteLine(" thread state: {0}", pt.ThreadState.ToString());
}
}
}
}
@HelgeJensen 提出了一个可行的解决方案: 如何监控 .NET TaskSchedulers 中的任务队列(跨 AppDomain)
Microsoft 不鼓励使用GetScheduledTasksForDebugger 方法 :
此方法不是线程安全的,您不应将它与其他 TaskScheduler 实例同时使用。仅当调试器已挂起所有其他线程时,才从 调试器调用此方法。
我测试了提议的解决方案并且它有效,即使我们可能会在生产代码中使用它遇到崩溃:
public static class ScheduledTaskAccess
{
public static Task[] GetScheduledTasksForDebugger(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]);
}
}
用法:
Task[] tasks = GetScheduledTasksForDebugger(TaskScheduler.Current);
int count = tasks.Length;