By: Artemakis Artemiou | Updated: 2019-03-26 | Comments | Related: > Application Development
Problem
In my previous Application Development tips, among other, we've talked about multithreading in .NET using different techniques such as using the BackgroundWorker class, as well as the Thread class. In this tip, we will be examining another multithreading technique in .NET, that is using the ThreadPool class.
Solution
The ThreadPool class in .NET, just like the Thread class, belongs to the System.Threading namespace, and allows to create and control a thread, set its priority, and get its status.
In this tip, we are going to write a .NET C# Console App, that will be performing the below:
- Read a CSV file with 10.000 lines and copy it to a second csv file.
- Along with the copy process, display the progress percentage.
The above operation will be implemented using multithreading via the ThreadPool class.
Sample CSV File
The below screenshot, illustrates the CSV file that we will copy into a new one via our .NET application.
OK, now let's proceed with creating our .NET C# Console Application.
Creating the .NET C# Console App
Let's start a new "Console App" project in Visual Studio 2017, name it "ThreadPoolExample", and save it in the folder "c:\tmp\demos" (you can use any folder you like):
Below, you can see the workspace in Visual Studio just right after the empty project is created.
Now, let's start adding some code.
The first thing to do is to add the proper namespaces, in order to allow us to make use of their classes.
Therefore, we are adding the System.Threading and System.IO namespaces:
//add the System.Threading namespace using System.Threading; //add the System.IO namespace using System.IO;
The System.Threading namespace will allow us to make use of the ThreadPool class, and the System.IO namespace will let us use all the necessary classes for performing the file copy operation.
OK, now let's take a look at the full source code for this example and then analyze it:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; //add the System.Threading namespace using System.Threading; //add the System.IO namespace using System.IO; namespace ThreadPoolExample { class Program { //Object used for passing parameters to the CopyFileThreadPool method class ThreadParams { public string sourceFileName { get; set; } public string targetFileName { get; set; } } static void Main(string[] args) { //custom thread parameter object ThreadParams tParams = new ThreadParams(); //get the file name from command prompt - user input tParams.sourceFileName = args[0].ToString(); tParams.targetFileName = args[1].ToString(); //queue the file copy thread for execution ThreadPool.QueueUserWorkItem(new WaitCallback(copyFileThreadPool), tParams); //we do not allow the main thread to terminate because it //will also terminate the file copy thread Console.ReadKey(); } private static void copyFileThreadPool(object tParams) { //read the custom thread parameter object ThreadParams tParamsInner = tParams as ThreadParams; string sourceFileName = @tParamsInner.sourceFileName; string targetFileName = @tParamsInner.targetFileName; Console.WriteLine(); Console.WriteLine("File Copy Operation using the ThreadPool Class"); Console.WriteLine(); Console.WriteLine("File to copy: " + @sourceFileName); //count file lines StreamReader sr = new StreamReader(@sourceFileName); float totalLines = File.ReadLines(@sourceFileName).Count(); sr.Close(); string line = ""; float progress = 0; Console.WriteLine(); Console.WriteLine("Copying file..."); int count = 0; //start reading the file and copying it to the second file using (StreamReader reader = new StreamReader(@tParamsInner.sourceFileName)) { StreamWriter writer = new StreamWriter(@targetFileName); while ((line = reader.ReadLine()) != null) { ++count; //avoid adding blank line in the end of file if (count != totalLines) writer.WriteLine(line); else writer.Write(line); //progress report progress = (count / totalLines) * 100; Console.WriteLine("Progress: " + Math.Round(progress, 2).ToString() + "% (rows copied: " + count.ToString() + ")"); } writer.Close(); } Console.WriteLine(); Console.WriteLine("Original File: " + @sourceFileName); Console.WriteLine("New File: " + @targetFileName); Console.WriteLine(); Console.WriteLine("press any key to exit..."); } } }
Code Analysis
Let's analyze the above code, in order to fully understand it.
The program's code consists of two methods and an inner class.
The inner class is named "ThreadParams" and has two string fields, that is "sourceFileName" and "targetFileName". The purpose of this class, is to be used as an input parameter in the "copyFileThreadPool" method.
Now, let's analyze the two methods of our program:
- copyFileThreadPool: This is the method that will be undertaking the file copy operation and which will run as a new thread. It consists of two main objects, that is a StreamReader that reads the original file, and a StreamWriter object, that is used for creating the new file based on the source. Moreover, the operation's progress is reported via this method. Another important thing to note about this method, is that it takes as an input parameter an object. This object, as you will see in the analysis of the method "Main", is actually the "ThreadParams" object.
- Main: This is
the main method of the program. It is actually the main thread. The first thing
we do in this method, is to instantiate a new object of the "ThreadParams"
class and populate its two fields "sourceFileName" and "targetFileName"
based on the user's input. Then we set our ThreadPool and queue the "copyFileThreadPool"
method for execution by actually creating a new thread and passing the "copyFileThreadPool"
method as a delegate parameter. Moreover, this is the time where we pass as
a parameter to the "copyFileThreadPool" method, the instantiated
object of the "ThreadParams" class, that is the "tParams" object. Last but
not least, we include a "
Console.ReadKey();"
command in order for our main thread, not to quit before the other thread completes its execution. Of course, if you enter a key before the file copy operation is completed, then the application will exit because the main thread will end its execution.
Running the Application
We run the application using the below command in the command prompt:
As you can see, the program runs and reports progress, and a few minutes later, the application completes the file copy operation and reports its final status.
Now, let's check the file that was created by executing our .NET Console App:
As you can see in the above screenshot, our multi-threaded .NET app, successfully copied the source file into a new one.
Conclusion
In this tip, we discussed about another multithreading technique in .NET, that is the ThreadPool class. We created a simple .NET C# Console App and in addition to the main thread, we have added to the ThreadPool queue, another thread that was used for undertaking a file copy operation.
By now, we have studied three different techniques for multithreading in .NET, that is using the BackgroundWorker class, the Thread class, and in this tip, the ThreadPool class.
In my next tip, we will discuss about parallelism in .NET and how we can easily launch parallel tasks, thus managing to develop a multithreading behavior.
Next Steps
- Check out my tip: Learn how to build a multithreading .NET Application to work with SQL Server
- Check out my tip: .NET Multithreading Example Using Thread Class
- Check out my tip: How to Get Started with SQL Server and .NET
- Check the MS Docs article: ThreadPool Class
- Check the MS Docs article: System.Threading Namespace
- Check the MS Docs article: System.IO Namespace
- Check the MS Docs article: Delegates (C# Programming Guide)
About the author
This author pledges the content of this article is based on professional experience and not AI generated.
View all my tips
Article Last Updated: 2019-03-26