如何获取当前C#程序所有线程的调用栈信息 ?

咨询区

  • Daniel Sperry

请问如何获取 .NET 程序当前所有线程的调用栈信息?我知道在 java 中只需调用 java.lang.Thread.getAllStackTraces() 方法即可。

回答区

  • Will Calderwood

在 .NET 中并不容易实现,但可以使用诊断库 ClrMD ,可以在 nuget 上下载,它可以获取到当前进程的所有线程栈信息的快照,当然还可以获取 线程名 等各种附加信息,太强大了,参考如下代码:

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();
        }
    }
}

点评区

其实是这样的,如何想自动化获取当前的进程中所有线程的调用栈,用 ClrMD 即可,如果是为了对程序进行分析诊断,可以借助 windbg,再使用 sos 中的 ~*e !clrstack 命令即可,比如下面这样:

0:000> ~*e !clrstack 
OS Thread Id: 0x4110 (0)
Child SP       IP Call Site
0019f3e4 77a2166c [InlinedCallFrame: 0019f3e4] 
0019f3e0 79b49b71 DomainNeutralILStubClass.IL_STUB_PInvoke(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr)
0019f3e4 7a27b275 [InlinedCallFrame: 0019f3e4] Microsoft.Win32.Win32Native.ReadFile(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr)
0019f448 7a27b275 System.IO.__ConsoleStream.ReadFileNative(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte[], Int32, Int32, Boolean, Boolean, Int32 ByRef)
0019f47c 7a27b17b System.IO.__ConsoleStream.Read(Byte[], Int32, Int32)
0019f49c 79b2e6a3 System.IO.StreamReader.ReadBuffer()
0019f4ac 79b2eb5b System.IO.StreamReader.ReadLine()
0019f4c8 7a3c3786 System.IO.TextReader+SyncTextReader.ReadLine()
0019f4d8 7a221845 System.Console.ReadLine()
0019f4e0 022f0983 *** WARNING: Unable to verify checksum for D:\net5\ConsoleApp1\ConsoleApp1\bin\Debug\ConsoleApp1.exe
ConsoleApp1.Program.Main(System.String[]) [D:\net5\ConsoleApp1\ConsoleApp1\Program.cs @ 25]
0019f67c 78e1f036 [GCFrame: 0019f67c] 
OS Thread Id: 0x11ac (24)
Child SP       IP Call Site
06c4f214 77a21bdc [HelperMethodFrame_1OBJ: 06c4f214] System.Threading.WaitHandle.WaitMultiple(System.Threading.WaitHandle[], Int32, Boolean, Boolean)
06c4f328 79ae8a86 System.Threading.WaitHandle.WaitAny(System.Threading.WaitHandle[], Int32, Boolean)
06c4f34c 7ace3f24 *** WARNING: Unable to verify checksum for C:\Windows\assembly\NativeImages_v4.0.30319_32\System\258d4259dd4377d917679ad4b058966e\System.ni.dll
System.Net.TimerThread.ThreadProc()
06c4f3a8 79a62e01 System.Threading.ThreadHelper.ThreadStart_Context(System.Object)
06c4f3b4 79a88604 System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
06c4f420 79a88537 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
06c4f434 79a884f4 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
06c4f44c 79a62d5b System.Threading.ThreadHelper.ThreadStart()
06c4f630 78e1f036 [GCFrame: 06c4f630] 
06c4f774 78e1f036 [DebuggerU2MCatchHandlerFrame: 06c4f774] 
OS Thread Id: 0x2fdc (25)
Child SP       IP Call Site
0700f114 755be695 [InlinedCallFrame: 0700f114] 
0700f110 7ad6aa01 DomainBoundILStubClass.IL_STUB_PInvoke(System.Net.SSPIHandle ByRef, System.Net.SecurityBufferDescriptor, UInt32, UInt32*)
0700f114 7ad530f4 [InlinedCallFrame: 0700f114] System.Net.UnsafeNclNativeMethods+NativeNTSSPI.DecryptMessage(System.Net.SSPIHandle ByRef, System.Net.SecurityBufferDescriptor, UInt32, UInt32*)
0700f154 7ad530f4 System.Net.SSPISecureChannelType.DecryptMessage(System.Net.SafeDeleteContext, System.Net.SecurityBufferDescriptor, UInt32)
0700f194 7ad51a1a System.Net.SSPIWrapper.EncryptDecryptHelper(OP, System.Net.SSPIInterface, System.Net.SafeDeleteContext, System.Net.SecurityBuffer[], UInt32)
0700f1fc 7ad52fe2 System.Net.Security.SecureChannel.Decrypt(Byte[], Int32 ByRef, Int32 ByRef)
0700f21c 7ad52e07 System.Net.Security._SslStream.ProcessFrameBody(Int32, Byte[], Int32, Int32, System.Net.AsyncProtocolRequest)
0700f248 7ad52d6b System.Net.Security._SslStream.ReadFrameCallback(System.Net.AsyncProtocolRequest)
0700f274 7ad4e576 System.Net.AsyncProtocolRequest.CompleteRequest(Int32)
0700f280 7ad4e537 System.Net.FixedSizeReader.CheckCompletionBeforeNextRead(Int32)
0700f28c 7ad4e4c6 System.Net.FixedSizeReader.ReadCallback(System.IAsyncResult)
0700f2b4 7ad14cf6 System.Net.LazyAsyncResult.Complete(IntPtr)
0700f2e8 7ad49d15 System.Net.ContextAwareResult.CompleteCallback(System.Object)
0700f2ec 79a88604 System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
0700f358 79a88537 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
0700f36c 79a884f4 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
0700f384 7ad4856d System.Net.ContextAwareResult.Complete(IntPtr)
0700f39c 7ad14c71 System.Net.LazyAsyncResult.ProtectedInvokeCallback(System.Object, IntPtr)
0700f3c4 7ad48378 System.Net.Sockets.BaseOverlappedAsyncResult.CompletionPortCallback(UInt32, UInt32, System.Threading.NativeOverlapped*)
0700f3f8 79aea3dd System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32, UInt32, System.Threading.NativeOverlapped*)
0700f4f4 78e1f036 [GCFrame: 0700f4f4] 
0700f604 78e1f036 [DebuggerU2MCatchHandlerFrame: 0700f604] 
OS Thread Id: 0x4214 (26)
Child SP       IP Call Site
GetFrameContext failed: 1
00000000 00000000 

0:000> !tp
CPU utilization: 13%
Worker Thread: Total: 13 Running: 0 Idle: 13 MaxLimit: 2047 MinLimit: 12
Work Request in Queue: 0
--------------------------------------
Number of Timers: 1
--------------------------------------
Completion Port Thread:Total: 16 Free: 6 MaxFree: 24 CurrentLimit: 16 MaxLimit: 1000 MinLimit: 12
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在C#中,可以使用Thread类来创建多个线程,并通过调用方法来启动这些线程。下面是一个简单的示例代码,演示如何创建和启动多个线程调用同一个方法: ```csharp using System; using System.Threading; class Program { static void Main(string[] args) { // 创建3个线程 Thread t1 = new Thread(new ThreadStart(MyMethod)); Thread t2 = new Thread(new ThreadStart(MyMethod)); Thread t3 = new Thread(new ThreadStart(MyMethod)); // 启动这些线程 t1.Start(); t2.Start(); t3.Start(); // 等待这些线程结束 t1.Join(); t2.Join(); t3.Join(); Console.WriteLine("所有线程已结束"); } static void MyMethod() { // 这里是你要执行的方法 Console.WriteLine("线程 {0} 正在执行 MyMethod", Thread.CurrentThread.ManagedThreadId); } } ``` 在这个示例中,我们创建了3个线程,并通过调用Thread.Start()方法来启动它们。这些线程都会调用同一个方法MyMethod(),并在控制台输出一条消息。最后,我们使用Thread.Join()方法等待这些线程结束,然后输出一条总结消息。 需要注意的是,多线程编程需要注意线程安全性,以免出现竞态条件等问题。在实际开发中,需要根据具体情况来选择适合的多线程编程模型和技术。 ### 回答2: C是一种通用的、高级的编程语言。它由美国贝尔实验室的丹尼斯·里奇(Dennis Ritchie)在1972年为了开发UNIX操作系统而设计出来。C语言以其高效性、简洁性和可移植性而闻名,并且是许多其他编程语言的基础。 C语言具有许多重要的特性。首先,它是一种静态类型的语言,这意味着变量的类型是在编译时确定的,从而使得编译器能够在编译时检测到许多错误。其次,C语言具有强大的指针功能,它允许程序员直接访问内存地址,从而使得对计算机硬件的底层操作变得可能。另外,C语言提供了丰富的库函数,使得程序员可以轻松地实现各种功能。 C语言的可移植性也是其成功的重要原因之一。由于C语言是一种面向过程的语言,它不依赖于任何特定的硬件或操作系统,因此可以在不同的平台上进行编译和运行。这使得开发人员可以轻松地将其代码移植到其他系统上,并使得C语言在计算机科学和软件工程领域广泛应用。 此外,C语言还是许多其他编程语言的基础。许多高级编程语言(如C++、Java和Python)都是基于C语言开发的,并使用了C语言的许多特性和语法。因此,掌握C语言将为学习和理解其他编程语言奠定坚实的基础。 总之,C语言是一种重要的编程语言,拥有高效性、简洁性和可移植性的特点。它不仅被广泛应用于计算机科学和软件工程领域,还是许多其他编程语言的基础。对于想要深入了解编程的人来说,学习C语言是一个很好的起点。 ### 回答3: C是计算机科学中一门重要的编程语言,它由贝尔实验室的丹尼斯·里奇在20世纪70年代初开发。C语言的设计目标是提供一种灵活、高效、可移植的编程语言,旨在用于编写底层的系统软件,同时也适用于开发各种应用程序。 C语言的特点之一是它的可移植性。C语言源代码可以编译成机器码,这使得它可以在不同的操作系统上运行,而不需要进行太多修改。这是因为C语言在底层硬件和操作系统之间提供了一种抽象的接口,使得程序员可以编写与特定平台无关的代码。 另一个C语言的特点是它的效率。C语言被设计成一种低级语言,允许程序员对内存和处理器进行直接的控制。这使得C语言非常适合编写需要高性能的应用程序,例如游戏引擎、嵌入式系统等。 此外,C语言还具备一些其他的优点。首先,C语言具有丰富的库函数,使得程序员可以快速地开发各种应用程序。其次,C语言支持面向对象的编程风格,通过结构体和指针,可以实现面向对象的概念。最后,C语言的语法简洁清晰,易于学习和理解。 总而言之,C语言在计算机科学中具有重要的地位。它是一种灵活、高效、可移植的编程语言,适用于开发各种类型的应用程序。无论是从事底层系统编程还是应用程序开发,学习和掌握C语言都是非常有益的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值