I am a software engineer. I have been working with C++, MFC, and net technologies for 15 years. I like video games and reading books.
1. Introduction to Thread
A "Thread" in programming language represents a lightweight version of a process with a comparatively small number of resources required for its operation. We know that a process is a set of "Microprocessor Instruction Sets," and CPU will execute these instruction sets. In modern multitasking operating systems like windows, there will be a higher number of processors running in parallel, and CPU will execute the instruction sets by allocating some time for each process.
The same "CPU Time Slicing" holds true for Threads as well. Like a process, a thread will have instruction sets associated to it and CPU will allocate it’s time for each thread. If there is more than one CPU, then there will be a chance of executing instructions from two different threads simultaneously. But, what is more common is that CPU time is allocated for each running process and threads spawned by it.
In this article, we will create an Windows Console Application which explains how we can create thread in C-Sharp. We will also look at the need for "Thread.Join()".
2. Counting Numbers Without Thread
First create C# Console Application and in the Program.cs file add the below code in the static void main function.
Here, we are using two variables called CountVar1, CountVar2. These variables are used to keep the running count.
After the variable declaration, we are making call to Console.WriteLine() to write informative text to the console output window. The Console.ReadLine() key is used to read the Enter Button keystroke from the user. This will allow Console Output Window to wait so that the user responds back by hitting the enter key. The code for this is shown below:
After the user responds back, we print two separate countings and display that in the Console Output window. First, we are setting the foreground color of the console output window to green by setting the ForegroundColor property. The predefined green color is taken from the ConsoleColor enumeration.
Once the console color is set to green, we are running a For Loop and printing the counting that goes till 999. Next, we are setting the Console windows output color to yellow and starting the second loop to print the counting from 0 to 999. After this, we are resetting the Console window to its original state. The code is below:
The two loops execution in the Main Thread context is shown in the below picture:
The picture above shows that CountVar1 loop is entered first and starts counting the variables and displays in the Console Windows. And the time taken for that is T1 milliseconds. The CountVar2 will wait for the exit of CountVar1 loop. Once CountVar1 loop exits, the CountVar2 loop starts and displays the output by taking T2 milliseconds. Here, the counting loops are sequential, and this can be proved by the program output at this stage. Run the Program as shown below from the command prompt:
The output of the program execution is shown below (The output is broken into three pieces).
In the above output, we can see that the loops executed sequentially and the yellow color console output can be seen only after the green (First Loop) one.
3. Loop Counting Functions for Thread
Now, we will move the loop counting to two different functions and assign each to a dedicated thread later. First, have a look at these functions:
In the above code, you can see that counting is similar to what we have seen previously. The two loops are converted into two different functions. However, you can see setting the ForgroundColor of Console Window is done inside the loop for a purpose.
Previously, we saw that the loops executed sequentially, and now, we are going to allocate a thread for each function, and the CPU will apply "Time slicing" (Try to execute instruction sets from both the function by scheduling its time. Nano Seconds?) so that it pays attention to both the loops. That is, the CPU spends some of its time with first function and some with second function while counting.
Keeping those in mind, in addition to both functions accessing the same resource (console window), the Foreground color setting is done inside loop. This will 99% shows first function output in green color and second function output in yellow color. What about the 1% error? We have to learn Thread Synchronization for that. And we will see that in a different article.
4. Creating Simple Threads and Starting It
To use thread in this example, a namespace is included and the code is shown below:
In the Main function using Console.WriteLine(), informative message is given to the user. The thread start begins, once the user hits the Enter Key button. The code is below:
After the informative message, we are creating two threads called T1 and T2 by supplying the static threaded functions created earlier. Have a look at the code below:
The above code snippet can be explained through the depiction below.
In the above picture, Marker 1 shows that we are holding the reference to the thread instance T1 of type “Thread”. Marker 2 shows that we are creating the “ThreadStart” delegate and supplying that to the constructor of the Thread class. Also note that we are creating the delegate by providing the function that runs on this thread T1. In the same way, we are making the CountVar2_Thread() function run on Thread instance T2.
Finally, we are starting the Threads by calling Start() method. The start method then invokes the delegate to call the supplied function. Now the function runs the thread, which is started by "Start()" method call. Have a look at the code below:
In the above code snippet, we are starting two threads, T1 and T2. After starting the Thread, we are printing an informative message in the Console Window. Note that the Main thread (The Main() function is running on the "Main Application Thread") spawned two Threads called T1 and T2. Now CountVar1_Thread() function is executed on Thread T1 and CountVar2_Thread() is executed on Thread T2. The timing of execution can be explained through the picture below:
The above timing chart shows that the Main thread started the Thread T1 first and then Thread T2. After a certain point in time, we can say that all three threads (Main, T1, T2) are served by the CPU by means of executing the instruction sets involved in it. This time period (All three threads are busy) is shown as yellow block. While threads T1 and T2 are busy counting the numbers & spitting them on the console window, the Main thread quits after printing the Resetting Console Window message. We can see a problem here. The intention is to reset the console window Foreground color to its original state after T1 and T2 finish. But the Main Thread continues its execution after spawning the thread and quits before T1 and T2 exit (Time t1 is well ahead of t2 & t3).
The Console.ResetColor(); called by the Main thread, is overwritten by T1 and T2, and whichever thread finishes last leaves the console window with the foreground color set by it. In the above picture, we can see even though the Main thread halts at time t1, Thread T1 continues till t2 and Thread T2 continues till t3. The green block shows T1 and T2 execution happening in parallel. We actually don’t know which thread will finish first (T1 or T2?). When all threads quit, the operating system removes the program from memory.
Have a look at the output of the program:
The above output shows that green thread (T1) finished counting first. And yellow thread finished last. The "dir command" lists the directory in yellow color as the Reset Console window done by Main thread is overwritten by the T1 and T2 multiple time.
5. Thread.Join() - The Calling Thread Waits...
The "Join()" method is useful to wait till other thread finishes the Task. Have a look at the code below:
The main thread calling T1.Join() states that the Main thread will wait till T1 finishes. The same way T2.Join() ensures that Main thread will till T2 finishes the job. When we call both T1.Join(); T2.Join(), Main thread will until T1 and T2 finish their counting. Look at the last line of code Console.ResetColor(). It is safe now, right?
The complete code example is given below:
© 2018 sirama