dmarra
dmarra

Reputation: 863

How to check that a thread is complete?

I am having a lot of trouble with this. Consider this example:

public class Test {
    Thread t;

    public Test() {
        t = new Thread(ThreadFunction);
    }

    public void Start() {
        t.Start();
    }

    private void ThreadFunction() {
        Thread.Sleep(5000);
        Console.WriteLine("Function Complete");
    }    
}


public static class Main {
    public Main() {
        Test test = new Test();
        test.Start();

        // sleep longer than my worker so it finishes
        Thread.Sleep(10000);

        // a place to place a breakpoint
        bool breakPointHere = true;
    }        
}

Now, I see the output of the console.log, but when I inspect Test's thread object, I see that IsAlive is still true, and ThreadStatus = TheadStatus.Running. Why is this? I wish to detect that the thread is truly complete, but I am confused as to how it can still be considered running if ThreadFunction() completes?

EDIT 2:

I finally tracked down the cause, Updating the code, and am going to answer my own question

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;


namespace ConsoleApplication1 {
    public abstract class Worker {
        protected bool shutdown;
        protected Thread t;

        private bool _isStopped = true;
        public bool IsStopped {
            get {
                return t.ThreadState == ThreadState.Stopped;
            }
        }

        private bool _isPaused = false;
        public bool IsPaused {
            get {
                return _isPaused;
            }
        }

        private string stringRepresentation;

        public Worker() {
            t = new Thread(ThreadFunction);
            stringRepresentation = "Thread id:" + t.ManagedThreadId;
            t.Name = stringRepresentation;
        }


        public void Start() {
            OnBeforeThreadStart();
            t.Start();
        }

        public void ScheduleStop() {
            shutdown = true;
        }

        public void SchedulePause() {
            OnPauseRequest();
            _isPaused = true;
        }

        public void Unpause() {
            _isPaused = false;
        }

        public void ForceStop() {
            t.Abort();
        }


        /// <summary>
        /// The main thread loop.
        /// </summary>
        private void ThreadFunction() {
            OnThreadStart();
            while (!shutdown) {
                if (!IsPaused) {
                    if (!OnLoop()) {
                        break;
                    }
                }
                Thread.Sleep(1000);
            }
            OnShutdown();
        }

        public abstract void OnBeforeThreadStart();
        public abstract void OnThreadStart();
        public abstract bool OnLoop();
        public abstract void OnShutdown();
        public abstract void OnPauseRequest();


        public override string ToString() {
            return stringRepresentation;
        }
    }


    public class Test : Worker {
        public override void OnBeforeThreadStart() {
            Log.WriteLine(this + ": Thread about to be started...");
        }

        public override void OnThreadStart() {
            Log.WriteLine(this + ": Thread Started!");
        }

        public override bool OnLoop() {
            Log.WriteLine(this + ": I am doing the things...");
            return true;
        }

        public override void OnShutdown() {
            Log.WriteLine(this + ": Shutting down!");
        }

        public override void OnPauseRequest() {            
        }
    }



public static class Log {
    public delegate void LogDelegate(string text, string eventTime, Severity severity);

    public static event LogDelegate OnWriteLine;

    private static Queue<string> _pendingFileWrites = new Queue<string>();


    public enum Severity {
        Info,
        Warning,
        Error
    }


    public static void WriteLine(object line, Severity severity = Severity.Info) {
        string eventTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
        string formatted = "[" + eventTime + "]: " + line;
        Console.WriteLine(formatted);

        lock (_pendingFileWrites) {
            _pendingFileWrites.Enqueue(formatted);
        }

        if (OnWriteLine != null) {
            // this is the offending line:
            OnWriteLine.Invoke((string)line, eventTime, severity);
        }
    }


    public static void WriteToFile(string path) {
        lock(_pendingFileWrites) {
            StreamWriter sw = File.AppendText(path);        
            while(_pendingFileWrites.Count > 0) {
                sw.WriteLine(
                    _pendingFileWrites.Dequeue()
                );
            }
            sw.Close();
        }   
    }
}



    class Program {
        static void Main(string[] args) {
            List<Test> tests = new List<Test>();
            for(int i = 0; i < 10; i++) {
                Test test = new Test();
                test.Start();
                tests.Add(test);
            }

            // sleep a little bit so they do the things
            Thread.Sleep(10000);

            foreach (Test test in tests) {
                test.ScheduleStop();
            }

            bool allStopped;
            do {
                allStopped = true;
                foreach (Test test in tests) {
                    if (!test.IsStopped) {
                        allStopped = false;
                        break;
                    }
                }
            } while (!allStopped);

            Console.WriteLine("Done!");

            // a place to place a breakpoint
            bool breakPointHere = true;
        }
    }
}

Upvotes: 0

Views: 212

Answers (3)

dmarra
dmarra

Reputation: 863

So it turns out that my issue was that my logging method was calling a UI thread function like so:

private void LogToForm(object line, string eventTime, Log.Severity severity) {
            if (dataGridView_LogInfo.InvokeRequired) {
                dataGridView_LogInfo.Invoke (
                    new Action<object, string, Log.Severity>(LogtoFormCallback), 
                    new object[] { line, eventTime, severity }
                );

            } else {
                LogtoFormCallback(line, eventTime, severity);
            }
        }

At the Invoke() line, the thread would hang forever. The solution was to replace it with BeginInvoke() instead.

EDIT: Also, my example was/is quite poor for this. I thought I didn't understand threads at a fundamental level, and that my examples would have been enough. Hopefully someone googles this though and has this same cause, and can try this solution.

Upvotes: 0

Scott Chamberlain
Scott Chamberlain

Reputation: 127543

I think your original testing that lead you to believe .IsAlive would be true had some flaw in it, I tweaked your program in your question to the following to make it compile and to be able to see which thread it created.

public class Program
{
    public class Test
    {
        Thread t;

        public Test()
        {
            t = new Thread(ThreadFunction);
            t.Name = "TestThread";
        }

        public void Start()
        {
            t.Start();
        }

        private void ThreadFunction()
        {
            Thread.Sleep(5000);
            Console.WriteLine("Function Complete");
        }
    }


    public static void Main()
    {
        Test test = new Test();
        test.Start();

        // sleep longer than my worker so it finishes
        Thread.Sleep(10000);

        // a place to place a breakpoint
        bool breakPointHere = true;
    }
}

here is a screenshot of the running threads from inside ThreadFunction

enter image description here

Here is a screenshot from the end of the program

enter image description here

Notice that there is no "TestThread" thread.

Here is a screenshot from the locals window

enter image description here

IsAlive is false.

Upvotes: 2

mariosangiorgio
mariosangiorgio

Reputation: 5543

Do you really need to sleep to wait for your thread to finish? If you don't, a better and more robust solution would be using Thread.Join()

public static class Main {
    public Main() {
        Test test = new Test();
        test.Start();

        test.Join(); // Waits for test to complete
        bool breakPointHere = true;
    }        
}

Upvotes: 1

Related Questions