How Threads and Foreground & Background Threads Work in C# .NET

In this article we’ll create threads and run those threads in parallel and we’ll also see how foreground and background threads work.

The execution of a C# program starts with a single thread called the main thread that is automatically run by the CLR and Operating System.

From the main thread, we can create other threads for doing the desired task in the program. The process of developing a program for execution with multiple threads is called multithreaded programming and the process of execution is called multithreading.

In C#  there're the following 2 kinds of threads.

  1. Foreground Thread
  2. Background Thread

1. Foreground Thread

  • Foreground threads are those threads that keep running even after the application exits or quits.
  • It has the ability to prevent the current application from terminating.
  • The CLR will not shut down the application until all Foreground Threads have stopped.

2. Background Thread

  • Background Threads are those threads that will quit if our main application quits. In short, if our main application quits, the background thread will also quit.
  • Background threads are views by the CLR and if all foreground threads have terminated, any and all background threads are automatically stopped when the application quits.

By default every thread we create is a Foreground Thread.

Let's look at an example.

In this example we'll create 2 Threads and will run those threads in parallell and we'll also see how Foreground and Background threads work.

Create a new Console project ForegroundBackgroundThread.

Program.cs

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using System.Threading;  
  6.   
  7. namespace ForegroundBackgroundThread  
  8. {  
  9.     class Program  
  10.     {  
  11.         static void Print1()  
  12.         {  
  13.             Console.WriteLine("Print1() running on {0} Thread", Thread.CurrentThread.Name);  
  14.             for (int i = 1; i < 10; i++)  
  15.             {  
  16.                 Console.WriteLine("Executing Print1.... ");  
  17.                 Thread.Sleep(1000);  
  18.             }  
  19.         }  
  20.   
  21.         static void Print2()  
  22.         {  
  23.             Console.WriteLine("Print2() running on {0} Thread", Thread.CurrentThread.Name);  
  24.             for (int i = 1; i < 10; i++)  
  25.             {  
  26.                 Console.WriteLine("Executing Print2....");  
  27.                 Thread.Sleep(1000);  
  28.             }  
  29.         }  
  30.   
  31.         static void Main(string[] args)  
  32.         {  
  33.             Console.Title = "ForegroundBackgroundThread";  
  34.             Thread t1 = new Thread(Print1);  
  35.             Thread t2 = new Thread(Print2);  
  36.   
  37.             t1.Name = "Primary";  
  38.             t2.Name = "Secondary";  
  39.   
  40.             Console.WriteLine("Select your desired option: \n");  
  41.             Console.WriteLine("1. To Run 2 Threads Parrelly");  
  42.             Console.WriteLine("2. Foreground Vs Background");  
  43.   
  44.             Console.Write("\nPlease enter your choice: ");  
  45.             int choice = int.Parse(Console.ReadLine());  
  46.             Console.WriteLine();  
  47.   
  48.             if (choice == 1)  
  49.             {  
  50.                 t1.Start();  
  51.                 t2.Start();  
  52.             }  
  53.   
  54.             if (choice == 2)  
  55.             {  
  56.                 Console.WriteLine("*********** Foreground Vs Background**********");  
  57.                 Console.WriteLine();  
  58.                 Console.WriteLine("Enter 1 to Start Foreground Thread and 2  Start Background Thread: ");  
  59.                 int number = int.Parse(Console.ReadLine());  
  60.                 Console.WriteLine();  
  61.                 switch (number)  
  62.                 {  
  63.                     case 1:  
  64.                         RunningForegroundThread();  
  65.                         break;  
  66.                     case 2:  
  67.                         RunningBackgroundThread();  
  68.                         break;  
  69.                     default:  
  70.                         break;  
  71.                 }  
  72.             }  
  73.          Console.WriteLine("Main() method completed...");  
  74.         }  
  75.   
  76.         static void Delay()  
  77.         {  
  78.             for (int i = 0; i < 10; i++)  
  79.             {  
  80.                 Console.WriteLine("Running......");  
  81.                 Thread.Sleep(1000);  
  82.             }  
  83.         }  
  84.   
  85.         static void RunningForegroundThread()  
  86.         {  
  87.             Thread foreground = new Thread(Delay);  
  88.             foreground.Start();  
  89.         }  
  90.   
  91.         static void RunningBackgroundThread()  
  92.         {  
  93.             Thread background = new Thread(Delay);  
  94.             background.IsBackground = true;  
  95.             background.Start();  
  96.         }  
  97.     }  

In the preceding example, we've created 2 functions, Print1() and Print2() and we'll run these 2 functions in parallel. RunningForegroundThread() and RunningBackgroundThread() are the functions used to do the Foreground and Background operations.

Let's begin with our Main method.

First we'll create 2 threads using the Thread class as shown below.
Thread class
Both of the threads are Foreground Threads.

We're passing ThreadStart as the parameter to the Thread class constructor. Here, ThreadStart points to the Print1() and Print2() functions to execute these threads.

If you right-click on ThreadStart and select “Go to Definition” then you'll get the Metadata of ThreadStart, where you find that ThreadStart is nothing but a delegate.
delegate
As we know, A Delegate is a type-safe function pointer that points to a function that the thread must execute.

All threads require an Entry Point to start its execution.

Any thread we create will need an explicitly defined entry point. So the thread always requires a delegate.

Next, create 2 methods (Print1 and Print2) for both the threads.

  1. static void Print1()  
  2. {  
  3.     Console.WriteLine("Print1() running on {0} Thread", Thread.CurrentThread.Name);  
  4.     for (int i = 1; i < 10; i++)  
  5.     {  
  6.         Console.WriteLine("Executing Print1.... ");  
  7.         Thread.Sleep(1000);  
  8.     }  
  9. }   
  10. static void Print2()  
  11. {  
  12.     Console.WriteLine("Print2() running on {0} Thread", Thread.CurrentThread.Name);  
  13.     for (int i = 1; i < 10; i++)  
  14.     {  
  15.         Console.WriteLine("Executing Print2....");  
  16.         Thread.Sleep(1000);  
  17.     }  

Both the methods print a Message 10 times, then after printing each message it uses the Thread.Sleep(1000) method to suspend the thread for 1 second.

After creating threads, use the Start() method to start our thread as shown below.
Start method
Our main application starts from the static void Main(string[] args), so that's out Main thread and we're getting input from the user to do their operation, in other words selecting 1 will run both the threads (t1 and t2) in parallel. Selecting 2 will show how the Foreground and Background Threads work.

If the user selects “To Run 2 Threads Parallely” then we'll get the following output where we can clearly see that both of the threads are running in parallel. Print1 is running on the Primary thread and Print2 is running on the Secondary thread.

thread
Now for the Foreground and Background Threads.

As in the above example, 2 methods are created RunningForegroundThread() and RunningBackgroundThread() to show how the Foreground and Background Thread work. The Delay() method also works the same as Print1 and Print2.

Both of the methods will create new threads (foreground and background).

To create a thread in the background make the IsBackground property true as shown below.
IsBackground
If the user selects the “Foreground and Background Thread” operation and selects 1, our Foreground thread starts and even if our application reaches the end of the line, in other words Console.WriteLine("Main() method completed..."); then our foreground thread will also continue to run until it completes their task, as shown in the following output.
Result
If the user selects 2 then our Background thread will start and as soon as the main thread reaches the end of line, in other words Console.WriteLine("Main() method completed..."); our background thread will quit without printing anything, as shown below.
printing

This is because the main thread that is our foreground thread terminated, the background thread is automatically killed.

Conclusion

Multithreading is very useful to develop efficient programs that could optimize the use of computer resources such as memory and time. If there is any mistake in this article then please notify me. Please provide your valuable feedback and comments, which enable me to provide a better article the next time.

Future Articles