Get list of threads

Solution 1:

Method 1: Get OS Threads

This gets the list of OS threads:

ProcessThreadCollection currentThreads = Process.GetCurrentProcess().Threads;

foreach (ProcessThread thread in currentThreads)
{
}

Method 2: Get Managed Threads

Managed threads sit on top of OS threads. The IDs are different, and in theory, more than one Managed Thread may sit on top of a single OS thread (although I havn't actually observed this).

It turns out that getting managed threads is trickier than it really should be.

Method 2.1: Simplest code to get Managed Threads

  1. Check out Microsoft.Diagnostics.Runtime on GitHub.
  2. Install NuGet package CLR Memory Diagnostics (ClrMD).

You can then use said NuGet package to attach to your own process, and read the managed threads out:

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)
    {
    }
}

Method 2.2: Example of how to search through managed threads by stack trace

Unfortunately, I couldn't find any way to search through the list of threads by the thread name.

However, all is not lost: here is an example of how to create a managed thread, then find it by searching through the stack frames for a match on the namespace, then print out its properties:

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

You can also find the correct match by saving ManagedThreadId within the thread that you create, then looking for this same ID in runtime.Threads.

Testing

Tested with all combinations of:

  • Visual Studio 2015 SP1
  • .NET 4.5
  • .NET 4.6.0
  • .NET 4.6.1
  • C# 5.0
  • C# 6.0

References

See ClrMd throws exception when creating runtime.

Solution 2:

using System.Diagnostics;

ProcessThreadCollection currentThreads = Process.GetCurrentProcess().Threads;

foreach (ProcessThread thread in currentThreads)    
{
   // Do whatever you need
}

Solution 3:

Updated code to get a snapshot of all stack traces that uses the answer from @Contango as a base. You'll still need to install NuGet package CLR Memory Diagnostics (ClrMD). This snippet also includes extra code to get the thread names, but this isn't required if you just want the stack traces.

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