views:

786

answers:

2

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.)

+1  A: 

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.

Winston Smith
Well, that's what it looks like I have to do, but I'd rather not.
Lasse V. Karlsen
+1  A: 

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 object m_locker = new object();
        private static readonly List<Thread> m_threadList = new List<Thread>();
        private readonly Thread m_thread;
        private readonly ParameterizedThreadStart m_start1;
        private readonly ThreadStart m_start2;

        public TrackedThread(ParameterizedThreadStart start)
        {
            this.m_start1 = start;
            this.m_thread = new Thread(this.StartThreadParameterized);
            lock (m_locker)
            {
                m_threadList.Add(this.m_thread);
            }
        }

        public TrackedThread(ThreadStart start)
        {
            this.m_start2 = start;
            this.m_thread = new Thread(this.StartThread);
            lock (m_locker)
            {
                m_threadList.Add(this.m_thread);
            }
        }

        public TrackedThread(ParameterizedThreadStart start, int maxStackSize)
        {
            this.m_start1 = start;
            this.m_thread = new Thread(this.StartThreadParameterized, maxStackSize);
            lock (m_locker)
            {
                m_threadList.Add(this.m_thread);
            }
        }

        public TrackedThread(ThreadStart start, int maxStackSize)
        {
            this.m_start2 = start;
            this.m_thread = new Thread(this.StartThread, maxStackSize);
            lock (m_locker)
            {
                m_threadList.Add(this.m_thread);
            }
        }

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

        public static IEnumerable<Thread> ThreadList
        {
            get
            {
                lock(m_locker)
                {
                    return new ReadOnlyCollection<Thread>(m_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.m_thread;
            }
        }

        private void StartThreadParameterized(object obj)
        {
            try
            {
                this.m_start1(obj);
            }
            finally
            {
                lock(m_locker)
                {
                    m_threadList.Remove(this.m_thread);
                }
            }
        }

        private void StartThread()
        {
            try
            {
                this.m_start2();
            }
            finally
            {
                lock (m_locker)
                {
                    m_threadList.Remove(this.m_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 class Program
    {
        private static void Main(string[] args)
        {
            TrackedThread thread1 = new TrackedThread(DoNothingForFiveSeconds);
            TrackedThread thread2 = new TrackedThread(DoNothingForTenSeconds);
            TrackedThread 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.

Jesse C. Slicer
Thanks, it looks like this is the route I will have to use.
Lasse V. Karlsen