我想列出所有正在运行的线程,但不使用List<>
该类。我想动态观察正在运行的线程。我怎样才能做到这一点?
问问题
62395 次
3 回答
69
方法一:获取操作系统线程
这将获取操作系统线程列表:
ProcessThreadCollection currentThreads = Process.GetCurrentProcess().Threads;
foreach (ProcessThread thread in currentThreads)
{
}
方法二:获取托管线程
托管线程位于操作系统线程之上。ID 是不同的,理论上,多个托管线程可能位于单个操作系统线程之上(尽管我实际上没有观察到这一点)。
事实证明,获得托管线程比实际应该的要棘手。
方法 2.1:获取托管线程的最简单代码
- 查看GitHub 上的 Microsoft.Diagnostics.Runtime。
- 安装 NuGet 包CLR 内存诊断 (ClrMD)。
然后,您可以使用所述 NuGet 包附加到您自己的进程,并读出托管线程:
using Microsoft.Diagnostics.Runtime;
using (DataTarget target = DataTarget.AttachToProcess(
Process.GetCurrentProcess().Id, 5000, AttachFlag.Passive))
{
ClrRuntime runtime = target.ClrVersions.First().CreateRuntime();
foreach (ClrThread thread in runtime.Threads)
{
}
}
方法 2.2:如何通过堆栈跟踪搜索托管线程的示例
不幸的是,我找不到任何方法来按线程名称搜索线程列表。
然而,一切并没有丢失:这是一个如何创建托管线程的示例,然后通过在堆栈帧中搜索命名空间上的匹配项来找到它,然后打印出它的属性:
namespace MyTest
{
int managedThreadId = 0;
var task = Task.Run(
() =>
{
// Unfortunately, cant see "Testing" anywhere in result returned
// from NuGet package ClrMD ...
Thread.CurrentThread.Name = "Testing";
Thread.Sleep(TimeSpan.FromDays(1));
});
// ... so we look for our thread by the first word in this namespace.
string startOfThisNamespace = this.GetType().Namespace.ToString().Split('.')[0]; // Is "MyTest".
using (DataTarget target = DataTarget.AttachToProcess(Process.GetCurrentProcess().Id, 5000, AttachFlag.Passive))
{
ClrRuntime runtime = target.ClrVersions.First().CreateRuntime();
foreach (ClrThread thread in runtime.Threads)
{
IList<ClrStackFrame> stackFrames = thread.StackTrace;
List<ClrStackFrame> stackframesRelatedToUs = stackFrames
.Where(o => o.Method != null && o.Method.ToString().StartsWith(startOfThisNamespace)).ToList();
if (stackframesRelatedToUs.Count > 0)
{
Console.Write("ManagedThreadId: {0}, OSThreadId: {1}, Thread: IsAlive: {2}, IsBackground: {3}:\n", thread.ManagedThreadId, thread.OSThreadId, thread.IsAlive, thread.IsBackground);
Console.Write("- Stack frames related namespace '{0}':\n", startOfThisNamespace);
foreach (var s in stackframesRelatedToUs)
{
Console.Write(" - StackFrame: {0}\n", s.Method.ToString());
}
}
}
}
}
您还可以通过保存ManagedThreadId
在您创建的线程中找到正确的匹配项,然后在runtime.Threads
.
测试
使用以下所有组合进行测试:
- 视觉工作室 2015 SP1
- .NET 4.5
- .NET 4.6.0
- .NET 4.6.1
- C# 5.0
- C# 6.0
参考
于 2016-02-22T16:02:36.383 回答
59
using System.Diagnostics;
ProcessThreadCollection currentThreads = Process.GetCurrentProcess().Threads;
foreach (ProcessThread thread in currentThreads)
{
// Do whatever you need
}
于 2012-04-25T12:52:20.090 回答
2
更新了代码以获取使用来自@Contango 的答案作为基础的所有堆栈跟踪的快照。您仍然需要安装 NuGet 包CLR Memory Diagnostics (ClrMD)。此代码段还包含用于获取线程名称的额外代码,但如果您只需要堆栈跟踪,则不需要这样做。
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Microsoft.Diagnostics.Runtime;
namespace CSharpUtils.wrc.utils.debugging
{
public static class StackTraceAnalysis
{
public static string GetAllStackTraces()
{
var result = new StringBuilder();
using (var target = DataTarget.CreateSnapshotAndAttach(Process.GetCurrentProcess().Id))
{
var runtime = target.ClrVersions.First().CreateRuntime();
// We can't get the thread name from the ClrThead objects, so we'll look for
// Thread instances on the heap and get the names from those.
var threadNameLookup = new Dictionary<int, string>();
foreach (var obj in runtime.Heap.EnumerateObjects())
{
if (!(obj.Type is null) && obj.Type.Name == "System.Threading.Thread")
{
var threadId = obj.ReadField<int>("m_ManagedThreadId");
var threadName = obj.ReadStringField("m_Name");
threadNameLookup[threadId] = threadName;
}
}
foreach (var thread in runtime.Threads)
{
threadNameLookup.TryGetValue(thread.ManagedThreadId, out string threadName);
result.AppendLine(
$"ManagedThreadId: {thread.ManagedThreadId}, Name: {threadName}, OSThreadId: {thread.OSThreadId}, Thread: IsAlive: {thread.IsAlive}, IsBackground: {thread.IsBackground}");
foreach (var clrStackFrame in thread.EnumerateStackTrace())
result.AppendLine($"{clrStackFrame.Method}");
}
}
return result.ToString();
}
}
}
于 2021-05-16T12:04:09.677 回答