Creating Simple Threads in C# and Role of Thread.Join() with Examples

Updated on March 12, 2018
sirama profile image

I am a software engineer. I have been working with C++, MFC, and .net technologies for 15 years. I like playing video games & reading books.

1. Introduction to Thread

A "Thread" in programming language represents a lightweight version of a process with comparatively small number resources required for its operation. We know that a process is set of "Microprocessor Instruction Sets" and CPU will execute these instruction sets. In modern Multi-Tasking Operating System like windows, there will be more 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 are more than one CPU then there will be chance of Executing instructions from two different thread 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.

//Sample 01: Lets start Two counting in a Loop
//1.1 Declarations
int CountVar1;
int CountVar2;

Here, we are using two variables called CountVar1, CountVar2. These variable 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 key stroke 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 below:

//1.2 Inform the User about the Counting
Console.WriteLine("Lets start two counting loops");
Console.WriteLine("Loop1 in Green");
Console.WriteLine("Loop2 in Yellow");
Console.WriteLine("Press Enter(Return) key to continue...");
Console.ReadLine();

After user responds back, we are printing two separate counting and displaying 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 enumaration.

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:

//1.3 Start Counting in the Main Thread
Console.WriteLine("Main Thread - Starts Counting");
Console.ForegroundColor = ConsoleColor.Green;
for (CountVar1 = 0;
    CountVar1 < 1000;
    CountVar1++)
{
    Console.WriteLine("CountVar1: " +
        CountVar1.ToString());
}
Console.ForegroundColor = ConsoleColor.Yellow;
for (CountVar2 = 0;
    CountVar2 < 1000;
    CountVar2++)
{
    Console.WriteLine("CountVar2: " + 
        CountVar2.ToString());
}
Console.ResetColor();
Console.WriteLine("Main Thread - After Counting Loops");

The two loops execution in the Main Thread context is shown in the below picture:

Two counting loops in Main Thread Context
Two counting loops in Main Thread Context | Source

The picture above shows that CountVar1 loop is entered first and start 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:

Run the SimpleThread from command line
Run the SimpleThread from command line | Source

The output of the program execution is shown below (The output is broken into three pieces)

Program Output: Loop Counting without Thread
Program Output: Loop Counting without Thread | Source

In the above output, we can see that the loops executed sequentially and 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:

//Sample 2.0: Counting functions used by Thread
//2.1: Counting Function for Thread 1
public static void CountVar1_Thread()
{
    for (int CountVar1 = 0; 
        CountVar1 < 1000; 
        CountVar1++)
    {
        Console.ForegroundColor = 
            ConsoleColor.Green;
        Console.WriteLine("CountVar1: " 
            + CountVar1.ToString());
    }
}

//2.2: Counting Function for Thread 2
public static void CountVar2_Thread()
{
    for (int CountVar2 = 0; 
        CountVar2 < 1000; 
        CountVar2++)
    {
        Console.ForegroundColor = 
            ConsoleColor.Yellow;
        Console.WriteLine("CountVar2: " 
            + CountVar2.ToString());
    }
}

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 spend some of its time with First Function and some with Second Function while doing the counting.

Keeping those in mind in addition with both function 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 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:

//Sample 03: NameSpace Required for Thread
using System.Threading;

In the Main function using Console.WriteLine(), informative message is given to the user. The thread start begins, once user hits the Enter Key button. Code is below:

//Sample 4.0: Start Two Counting Loops 
//              in a separate thread
Console.WriteLine("Lets start two counting" + 
                   " loops in Threads");
Console.WriteLine("Thread1 in Green");
Console.WriteLine("Thread2 in Yellow");
Console.WriteLine("Press Enter(Return) key " + 
    "to continue...");
Console.ReadLine();

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:

//4.1 Create Two Separate Threads
Console.WriteLine("Main Thread - Before Starting Thread");
Thread T1 = new Thread(
    new ThreadStart(CountVar1_Thread));
Thread T2 = new Thread(
    new ThreadStart(CountVar2_Thread));

The above code snippet can be explained through the depiction below.

Creating Simple Threads in C#
Creating Simple Threads in C# | Source

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. The same way we are making the CountVar2_Thread() function to run on Thread instance T2.

Finally we are starting the Threads by calling Start() method. The start method then invoke 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:

//4.2 Start the Threads
T1.Start();
T2.Start();
Console.WriteLine("Main Thread - After Starting Threads");
Console.ResetColor();

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:

Thread Timing Chart - (Simulated one for Explanation)
Thread Timing Chart - (Simulated one for Explanation) | Source

The above timing chart shows that Main thread started the Thread T1 first and then Thread T2. After 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 thread T1 and T2 are busy in counting the numbers & spitting it on the console window, 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 finishes. But, the Main Thread continues its execution after spawning the thread and quits before T1 and T2 exits (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 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 thread quits, Operating system removes the program from memory.

Have a look at the output of the program:

Program Output: Counter Threads
Program Output: Counter Threads | Source

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:

//4.3 Reset the Console Window
T1.Join();
T2.Join();
Console.ResetColor();

The main thread calling T1.Join() states that 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 till T1 and T2 finishes their counting. Look at the last line of code Console.ResetColor(). It is safe now Right?

The complete code example is given below:

using System;
using System.Collections.Generic;
using System.Text;

//Sample 03: NameSpace Required for Thread
using System.Threading;

namespace SimpleThread
{
    class Program
    {
        //Sample 2.0: Counting functions used by Thread
        //2.1: Counting Function for Thread 1
        public static void CountVar1_Thread()
        {
            for (int CountVar1 = 0; 
                CountVar1 < 1000; 
                CountVar1++)
            {
                Console.ForegroundColor = 
                    ConsoleColor.Green;
                Console.WriteLine("CountVar1: " 
                    + CountVar1.ToString());
            }
        }

        //2.2: Counting Function for Thread 2
        public static void CountVar2_Thread()
        {
            for (int CountVar2 = 0;
                CountVar2 < 1000;
                CountVar2++)
            {
                Console.ForegroundColor =
                    ConsoleColor.Yellow;
                Console.WriteLine("CountVar2: "
                    + CountVar2.ToString());
            }
        }

        static void Main(string[] args)
        {
            //Sample 01: Lets start Two counting in a Loop
            //1.1 Declarations
            int CountVar1;
            int CountVar2;

            //1.2 Inform the User about the Counting
            Console.WriteLine("Lets start two counting loops");
            Console.WriteLine("Loop1 in Green");
            Console.WriteLine("Loop2 in Yellow");
            Console.WriteLine("Press Enter(Return) key to continue...");
            Console.ReadLine();

            //1.3 Start Counting in the Main Thread
            Console.WriteLine("Main Thread - Starts Counting");
            Console.ForegroundColor = ConsoleColor.Green;
            for (CountVar1 = 0;
                CountVar1 < 1000;
                CountVar1++)
            {
                Console.WriteLine("CountVar1: " +
                    CountVar1.ToString());
            }
            Console.ForegroundColor = ConsoleColor.Yellow;
            for (CountVar2 = 0;
                CountVar2 < 1000;
                CountVar2++)
            {
                Console.WriteLine("CountVar2: " +
                    CountVar2.ToString());
            }
            Console.ResetColor();
            Console.WriteLine("Main Thread - After Counting Loops");

            //Sample 4.0: Start Two Counting Loops 
            //              in a separate thread
            Console.WriteLine("Lets start two counting" +
                               " loops in Threads");
            Console.WriteLine("Thread1 in Green");
            Console.WriteLine("Thread2 in Yellow");
            Console.WriteLine("Press Enter(Return) key " +
                "to continue...");
            Console.ReadLine();

            //4.1 Create Two Separate Threads
            Console.WriteLine("Main Thread - Before Starting Thread");
            Thread T1 = new Thread(
                new ThreadStart(CountVar1_Thread));
            Thread T2 = new Thread(
                new ThreadStart(CountVar2_Thread));

            //4.2 Start the Threads
            T1.Start();
            T2.Start();
            Console.WriteLine("Main Thread - After Starting Threads");

            //4.3 Reset the Console Window
            T1.Join();
            T2.Join();
            Console.ResetColor();
        }
            
    }
}

© 2018 sirama

Comments

    0 of 8192 characters used
    Post Comment

    No comments yet.

    working

    This website uses cookies

    As a user in the EEA, your approval is needed on a few things. To provide a better website experience, owlcation.com uses cookies (and other similar technologies) and may collect, process, and share personal data. Please choose which areas of our service you consent to our doing so.

    For more information on managing or withdrawing consents and how we handle data, visit our Privacy Policy at: https://owlcation.com/privacy-policy#gdpr

    Show Details
    Necessary
    HubPages Device IDThis is used to identify particular browsers or devices when the access the service, and is used for security reasons.
    LoginThis is necessary to sign in to the HubPages Service.
    Google RecaptchaThis is used to prevent bots and spam. (Privacy Policy)
    AkismetThis is used to detect comment spam. (Privacy Policy)
    HubPages Google AnalyticsThis is used to provide data on traffic to our website, all personally identifyable data is anonymized. (Privacy Policy)
    HubPages Traffic PixelThis is used to collect data on traffic to articles and other pages on our site. Unless you are signed in to a HubPages account, all personally identifiable information is anonymized.
    Amazon Web ServicesThis is a cloud services platform that we used to host our service. (Privacy Policy)
    CloudflareThis is a cloud CDN service that we use to efficiently deliver files required for our service to operate such as javascript, cascading style sheets, images, and videos. (Privacy Policy)
    Google Hosted LibrariesJavascript software libraries such as jQuery are loaded at endpoints on the googleapis.com or gstatic.com domains, for performance and efficiency reasons. (Privacy Policy)
    Features
    Google Custom SearchThis is feature allows you to search the site. (Privacy Policy)
    Google MapsSome articles have Google Maps embedded in them. (Privacy Policy)
    Google ChartsThis is used to display charts and graphs on articles and the author center. (Privacy Policy)
    Google AdSense Host APIThis service allows you to sign up for or associate a Google AdSense account with HubPages, so that you can earn money from ads on your articles. No data is shared unless you engage with this feature. (Privacy Policy)
    Google YouTubeSome articles have YouTube videos embedded in them. (Privacy Policy)
    VimeoSome articles have Vimeo videos embedded in them. (Privacy Policy)
    PaypalThis is used for a registered author who enrolls in the HubPages Earnings program and requests to be paid via PayPal. No data is shared with Paypal unless you engage with this feature. (Privacy Policy)
    Facebook LoginYou can use this to streamline signing up for, or signing in to your Hubpages account. No data is shared with Facebook unless you engage with this feature. (Privacy Policy)
    MavenThis supports the Maven widget and search functionality. (Privacy Policy)
    Marketing
    Google AdSenseThis is an ad network. (Privacy Policy)
    Google DoubleClickGoogle provides ad serving technology and runs an ad network. (Privacy Policy)
    Index ExchangeThis is an ad network. (Privacy Policy)
    SovrnThis is an ad network. (Privacy Policy)
    Facebook AdsThis is an ad network. (Privacy Policy)
    Amazon Unified Ad MarketplaceThis is an ad network. (Privacy Policy)
    AppNexusThis is an ad network. (Privacy Policy)
    OpenxThis is an ad network. (Privacy Policy)
    Rubicon ProjectThis is an ad network. (Privacy Policy)
    TripleLiftThis is an ad network. (Privacy Policy)
    Say MediaWe partner with Say Media to deliver ad campaigns on our sites. (Privacy Policy)
    Remarketing PixelsWe may use remarketing pixels from advertising networks such as Google AdWords, Bing Ads, and Facebook in order to advertise the HubPages Service to people that have visited our sites.
    Conversion Tracking PixelsWe may use conversion tracking pixels from advertising networks such as Google AdWords, Bing Ads, and Facebook in order to identify when an advertisement has successfully resulted in the desired action, such as signing up for the HubPages Service or publishing an article on the HubPages Service.
    Statistics
    Author Google AnalyticsThis is used to provide traffic data and reports to the authors of articles on the HubPages Service. (Privacy Policy)
    ComscoreComScore is a media measurement and analytics company providing marketing data and analytics to enterprises, media and advertising agencies, and publishers. Non-consent will result in ComScore only processing obfuscated personal data. (Privacy Policy)
    Amazon Tracking PixelSome articles display amazon products as part of the Amazon Affiliate program, this pixel provides traffic statistics for those products (Privacy Policy)