Getting list of currently active managed threads in .NET?

25,104

Solution 1

If you're willing to replace your application's Thread creations with another wrapper class, said wrapper class can track the active and inactive Threads for you. Here's a minimal workable shell of such a wrapper:

namespace ThreadTracker
{
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Threading;

    public class TrackedThread
    {
        private static readonly IList<Thread> threadList = new List<Thread>();

        private readonly Thread thread;

        private readonly ParameterizedThreadStart start1;

        private readonly ThreadStart start2;

        public TrackedThread(ParameterizedThreadStart start)
        {
            this.start1 = start;
            this.thread = new Thread(this.StartThreadParameterized);
            lock (threadList)
            {
                threadList.Add(this.thread);
            }
        }

        public TrackedThread(ThreadStart start)
        {
            this.start2 = start;
            this.thread = new Thread(this.StartThread);
            lock (threadList)
            {
                threadList.Add(this.thread);
            }
        }

        public TrackedThread(ParameterizedThreadStart start, int maxStackSize)
        {
            this.start1 = start;
            this.thread = new Thread(this.StartThreadParameterized, maxStackSize);
            lock (threadList)
            {
                threadList.Add(this.thread);
            }
        }

        public TrackedThread(ThreadStart start, int maxStackSize)
        {
            this.start2 = start;
            this.thread = new Thread(this.StartThread, maxStackSize);
            lock (threadList)
            {
                threadList.Add(this.thread);
            }
        }

        public static int Count
        {
            get
            {
                lock (threadList)
                {
                    return threadList.Count;
                }
            }
        }

        public static IEnumerable<Thread> ThreadList
        {
            get
            {
                lock (threadList)
                {
                    return new ReadOnlyCollection<Thread>(threadList);
                }
            }
        }

        // either: (a) expose the thread object itself via a property or,
        // (b) expose the other Thread public methods you need to replicate.
        // This example uses (a).
        public Thread Thread
        {
            get
            {
                return this.thread;
            }
        }

        private void StartThreadParameterized(object obj)
        {
            try
            {
                this.start1(obj);
            }
            finally
            {
                lock (threadList)
                {
                    threadList.Remove(this.thread);
                }
            }
        }

        private void StartThread()
        {
            try
            {
                this.start2();
            }
            finally
            {
                lock (threadList)
                {
                    threadList.Remove(this.thread);
                }
            }
        }
    }
}

and a quick test driver of it (note I do not iterate over the list of threads, merely get the count in the list):

namespace ThreadTracker
{
    using System;
    using System.Threading;

    internal static class Program
    {
        private static void Main()
        {
            var thread1 = new TrackedThread(DoNothingForFiveSeconds);
            var thread2 = new TrackedThread(DoNothingForTenSeconds);
            var thread3 = new TrackedThread(DoNothingForSomeTime);

            thread1.Thread.Start();
            thread2.Thread.Start();
            thread3.Thread.Start(15);
            while (TrackedThread.Count > 0)
            {
                Console.WriteLine(TrackedThread.Count);
            }

            Console.ReadLine();
        }

        private static void DoNothingForFiveSeconds()
        {
            Thread.Sleep(5000);
        }

        private static void DoNothingForTenSeconds()
        {
            Thread.Sleep(10000);
        }

        private static void DoNothingForSomeTime(object seconds)
        {
            Thread.Sleep(1000 * (int)seconds);
        }
    }
}

Not sure if you can go such a route, but it will accomplish the goal if you're able to incorporate at an early stage of development.

Solution 2

Is it feasible for you to store thread information in a lookup as you create each thread in your application?

As each thread starts, you can get its ID using AppDomain.GetCurrentThreadId(). Later, you can use this to cross reference with the data returned from Process.Threads.

Share:
25,104
Lasse V. Karlsen
Author by

Lasse V. Karlsen

71-model software architect working for Visma, living in Porsgrunn, Telemark, Norway.

Updated on July 09, 2022

Comments

  • Lasse V. Karlsen
    Lasse V. Karlsen almost 2 years

    For a "log information for support" type of function I'd like to enumerate and dump active thread information.

    I'm well aware of the fact that race conditions can make this information semi-inaccurate, but I'd like to try to get the best possible result, even if it isn't 100% accurate.

    I looked at Process.Threads, but it returns ProcessThread objects, I'd like to have a collection of Thread objects, so that I can log their name, and whether they're background threads or not.

    Is there such a collection available, even if it is just a snapshot of the active threads when I call it?

    ie.

    Thread[] activeThreads = ??
    

    Note, to be clear, I am not asking about Process.Threads, this collection gives me a lot, but not all of what I want. I want to know how much time specific named threads in our application is currently using (which means I will have to look at connecting the two types of objects later, but the names is more important than the CPU time to begin with.)

  • Lasse V. Karlsen
    Lasse V. Karlsen over 14 years
    Well, that's what it looks like I have to do, but I'd rather not.
  • Lasse V. Karlsen
    Lasse V. Karlsen over 14 years
    Thanks, it looks like this is the route I will have to use.