CLR Threads Via C# Part 1 – Thread Creation And The System.Threading.Thread Class

In this post I cover the basics of CLR threads, the System.Threading.Thread class, and thread creation and lifecycle in C# via the CLR, this is part 1 in a multi-part series on threading in the CLR.

Advertisements

In a previous post I covered the Windows threading model, with some very slight coverage on CLR threads. In this series of posts, I want to cover in much more detail all of the various multi-threading functionality that the CLR provides. In this first post, I will be covering basic thread creation, thread lifetime, and the System.Threading.Thead class.

Thread creation

To instantiate a new thread in C#, you use the System.Threading.Thread class. This class represents a managed thread, and if you go to the definition of the Thread class, you can see a few things. First, we have the following constructors:

public Thread(ThreadStart start);
public Thread(ParameterizedThreadStart start);
public Thread(ThreadStart start, int maxStackSize);
public Thread(ParameterizedThreadStart start, int maxStackSize);

All of these constructors take some form of a delegate method (thread start) to be executed, and some of these constructors use ParameterizedThreadStart, which is a delegate that you can pass a piece of data to. Some also allow you to pass in a max stack size, which is, as you probably guessed, the max size of a threads thread stack. A couple of comments for this constructor tell us some rules for this type of constructor, for example if we specify a stack size larger than the default stack size, the size passed is ignored without exceptions being thrown, or we can pass 0 to default to the max thread stack size. We also get a list of exceptions thrown, which in the case of a thread can be the thread start being null, or the stack size being negative. These are relatively small, but important things to know. Let’s take a look at creating a very simple thread that simply takes a delegate method to be run:

        public static void RandomMethodToRun()
        {
            Console.WriteLine("Hello from a new thread!");
        }

        static void Main(string[] args)
        {
            Thread t = new Thread(RandomMethodToRun);
            t.Start();
            Console.ReadLine();
        }

Here, we instantiate the thread and pass it our method to be run, start it, and make a call to read line so we can see the result. Let’s modify this program a little bit as follows:

        public static void RandomMethodToRun(object threadNumber)
        {
            Console.WriteLine("Hello from thread {0}!", threadNumber);
        }

        static void Main(string[] args)
        {
            Thread t1 = new Thread(RandomMethodToRun);
            t1.Name = "FirstBornThread";
            t1.Start(1);

            Thread t2 = new Thread(RandomMethodToRun);
            t2.Name = "SecondBornThread";
            t2.Start(2);
            Console.ReadLine();
        }

Here, we create two threads using a parameterized thread start, and we pass in a number representing each thread. To pass in the data, note that my delegate method simply takes a generic object, and to pass in the data, you pass it via Thread.Start(). Just to show you can do this, I also named the threads so we could track them in the debugger if we decided we wanted to do that. When I run this program, I get the following output:

Hello from thread 2!
Hello from thread 1!

Note that even though we started these threads in order of thread 1, then thread 2, thread 2 got executed first, that’s because without locking mechanisms, threads are free to run in whatever order the task scheduler decides to queue them in, in this instance, it was the case that thread 2 ran first. Just to confirm this behavior is truly random, I ran the program several more times and eventually I got the following:

Hello from thread 1!
Hello from thread 2!

So we can confirm for sure that the underlying threads execute in random order.

Background vs. foreground threads

Threads in the CLR can be marked as background or foreground threads. Foreground threads are threads that must be executed before the program is allowed to shut down, and background threads are threads that don’t block the program from being shutdown. By default, instances of the thread class are marked as foreground threads, you can override this by setting the threads IsBackground property. To illustrate how background threads vs. foreground threads work, I have modified our delegate method as follows:

        public static void RandomMethodToRun(object threadNumber)
        {
            Thread.Sleep(5000);
            Console.WriteLine("Hello from thread {0}!", threadNumber);
        }

This will cause the thread to sleep for 5 seconds before it executes the code it needs to execute. So how does this prove foreground threads block shutdown? Well, note that since we are using threads separate from our main thread, the UI is still active, meaning IO can come in, so our final Console.ReadLine will still be hit, and we can give the program input via the main thread. The program is set to where it should exit when Console.ReadLine is called, except in this case it won’t, because the threads we spawned are still executing the code they were supposed to execute. When I run the program, sure enough, I hit enter over and over and over again, and nothing happens. Then, when the threads actually finish executing, the program closes some time later.

That still doesn’t prove this behavior is unique to foreground threads only, so let’s mark these threads as background threads and run the program again:

            Thread t1 = new Thread(RandomMethodToRun);
            t1.Name = "FirstBornThread";
            t1.IsBackground = true;
            t1.Start(1);

            Thread t2 = new Thread(RandomMethodToRun);
            t2.Name = "SecondBornThread";
            t2.IsBackground = true;
            t2.Start(2);

Now when I run this program, the readline gets hit, takes the input, and closes right away. So there you have it, foreground threads block programs from exiting until they finish, background threads don’t. This is really crucial to understand, because some threads, such as thread pool threads, are background threads, so if you need to do some mission critical stuff before a program exits and you do it with a thread pool thread thinking the program will wait to exit while that thread does it’s thing, you’re going to be really disappointed.

Thread lifetime

It’s also important to understand a threads lifetime in the CLR, meaning, when a thread will wrap up it’s work. Threads will execute until one of two things occur, either the delegate method it was executing returns, or a synchronous/asynchronous exception occurs. Synchronous exceptions occur whenever the thread itself encounters an exception, whereas an asynchronous exception occurs whenever another thread uses an interrupt or an abort on the thread. To check the current status of a thread in terms of whether it’s alive or not, you can check the IsAlive property of the thread:

            Thread t1 = new Thread(RandomMethodToRun);
            t1.Name = "FirstBornThread";
            bool t1StatusPreExecute = t1.IsAlive;
            t1.Start(1);
            bool t1StatusPostExecute = t1.IsAlive;

Here I just store the state of the thread pre and post start. Obviously pre-start, the thread is dead, and post-start, the thread is alive. It’s also worth noting that as I stepped through the debugger, if I waited a few seconds before hitting the next threads start and checked the first threads status, it was dead because it wrapped up execution. If you really need to know to a very accurate degree whether a thread is dead or alive, the IsAlive property doesn’t have the most granular accuracy. Typically, you want to check a threads status to base execution of something else on that threads status, and as we’ve stated moments ago, IsAlive isn’t exactly perfect for this purpose. To do this with more accuracy, you can use the threads Join method. Join will block the main thread until the thread join was called has exited. I won’t go into detail on using this method as I find it to not be the best way to solve this problem, locks are a far better option in my opinion and that’s what i’m going to focus on in a later post.

Stopping a thread

There are a few ways you can prematurely kill a thread. One such method is the Abort method, this method raises a ThreadAbortException, which begins the process of killing the thread. I’ve modified our program as follows to demonstrate this in action:

        public static void RandomMethodToRun(object threadNumber)
        {
            Thread.Sleep(1000);
            Console.WriteLine("Thread {0} executed to completion", threadNumber);
        }

        

        static void Main(string[] args)
        {
            Thread t1 = new Thread(RandomMethodToRun);
            t1.Start(1);

            Thread t2 = new Thread(RandomMethodToRun);
            t2.Start(2);

            Thread t3 = new Thread(RandomMethodToRun);
            t3.Start(3);
            t3.Abort();

            Thread t4 = new Thread(RandomMethodToRun);
            t4.Start(4);

            Thread t5 = new Thread(RandomMethodToRun);
            t5.Start(5);

            Thread t6 = new Thread(RandomMethodToRun);
            t6.Start(6);

            Console.ReadLine();
        }

This simply queues up 6 threads, and a Thread.Sleep sleeps the thread in the delegate method for 1 second, giving us enough time to ensure that the abort method can execute before the thread finishes it’s work. When I run this program, I get the following output:

Thread 1 executed to completion
Thread 2 executed to completion
Thread 5 executed to completion
Thread 4 executed to completion
Thread 6 executed to completion

This confirms that thread 3 did infact get terminated before it could finish. You can use this method to terminate a thread that’s been blocked (something I’ll cover in more detail in a post on CLR locking mechanisms), but you can also just use it like we did to kill a thread for any reason at all. Another built in method for stopping a thread is the Thread.Interrupt method, and this method is used to kill a thread in a wait/sleep/join state, it should be noted that this method doesn’t work for unmanaged threads. Let’s add the following code right after we start t1 and see what happens:

            t1.Interrupt();

When I run this, I get the following exception:

System.Threading.ThreadInterruptedException: 'Thread was interrupted from a waiting state.'

Why did this occur? Because using interrupt throws an exception that you have to handle in your code if it tries to interrupt a thread in a wait state (or join, etc.) Let’s modify the code a bit to handle the exception, here I’m just logging the exception:

        public static void RandomMethodToRun(object threadNumber)
        {
            try
            {
                Thread.Sleep(1000);
                Console.WriteLine("Thread {0} executed to completion", threadNumber);
            }
            catch(ThreadInterruptedException e)
            {
                Console.WriteLine(e.Message);
            }

        }

Running this code, I get the following output (note I still have the abort method for thread 3 in place):

Thread was interrupted from a waiting state.
Thread 2 executed to completion
Thread 4 executed to completion
Thread 6 executed to completion
Thread 5 executed to completion

Notice that the exception was logged instantly, because right after we start the thread and sleep it, we interrupt it right off the bat, so the exception is caught right away, while the other threads complete as normal, which takes at least 1 second.

So which of these should you use to kill threads? Well…ideally neither. These are terrible ways of killing threads, you should always opt for some sort of locking mechanism that avoids needing to wait threads.

Thread priority

I’ve already covered this in my previous post on threading, so check that out for more info on Windows process and thread priorities, but basically you can set a priority level that gives your thread a higher chance to be selected over other threads to get time with the CPU by the task scheduler. I’ve added priorities to some of the threads as follows:

t2.Priority = ThreadPriority.Lowest;
t4.Priority = ThreadPriority.Lowest;
t5.Priority = ThreadPriority.Lowest;
t6.Priority = ThreadPriority.Highest;

In this case, the tasks finish so quickly that no difference is really perceived, but in some circumstances, setting higher priority will benefit you. Note that by setting max priority to both the thread level and process level, you will literally take priority over things like network IO, user interface devices like mice and keyboard IO, etc. so don’t do that unless it’s appropriate (it probably isn’t).

Wrap up

There’s more to the Thread class that I didn’t cover, mainly due to the fact that those things are either deprecated, like Resume, Suspend, etc. or they were just more niche. Hopefully you found this post beneficial, and if you like this and want more, leave a comment, like the post, become an email follower or follow the blog, etc. because I have more coming soon. Thanks for taking the time to read this, and until next time.

  1. “threads are free to run in whatever order the task scheduler decides to queue them in”

    I think it’s confusing to call the Windows scheduler for threads “task scheduler” since that term has a different meaning in .Net (System.Threading.TaskScheduler) and also in Windows (Task Scheduler a.k.a. taskschd.msc).

    Like

    Reply

    1. Interesting, I hadn’t even considered this…will revise.

      Like

      Reply

  2. […] on April 8, 2018by admin submitted by /u/Trevor266 [link] [comments] No comments […]

    Like

    Reply

  3. […] the last post I talked about the basics of CLR threads by taking a look at the Thread class. We discussed […]

    Like

    Reply

  4. […] the last post, we discussed the thread pool, and in the post prior to that, we discussed the System.Threading.Thread class. In both of these posts, we’ve look at pretty […]

    Like

    Reply

  5. […] A look at CLR threads via the System.Threading.Thread class […]

    Like

    Reply

  6. […] CLR Threads Via C# Part 1 – Thread Creation And The System.Threading.Thread Class – Trevor Hart […]

    Like

    Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Advertisements
Advertisements
%d bloggers like this: