By: Artemakis Artemiou | Updated: 2019-03-27 | Comments | Related: > Application Development
Problem
In my previous Application Development tips, among other, we've talked about different techniques for implementing multithreading in .NET, such as using the BackgroundWorker class, the Thread class and the ThreadPool class.
In this tip, we will be examining another multithreading technique in .NET, that is the Task Parallel Library, also known as TPL.
Solution
The Task Parallel Library (TPL) in .NET, is a set of public types and APIs in the System.Threading and System.Threading.Tasks namespaces. TPL has been created in order to help developers to easily add parallelism and concurrency in their .NET applications.
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 Task Parallel Library (TPL).
Sample CSV File
The below screenshot, illustrates the source CSV file that we will be copying into a new one via our .NET application, using TPL.
OK, now let's proceed with the creation of the .NET C# Console Application.
Creating the .NET C# Console App
Let's start a new "Console App (.NET Framework)" project in Visual Studio 2017, name it "TPLExample", 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.
Since this is a console app and we don't have any GUI components to add, we directly start adding code.
The first step is to add the proper namespaces, in order to allow us to make use of the required classes in our code:
//add the System.Threading namespace using System.Threading; //add the System.IO namespace using System.IO;
As you can see, I have added two namespaces, the "System.Threading" namespace which will allow us to make use of TPL, and the System.IO namespace which will allow us to perform I/O operations, that is reading the source file and copying it into the new one.
Code Analysis
Now, let's see the full source code and further 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 TPLExample { class Program { static string sourceFileName = ""; static string targetFileName = ""; static void Main(string[] args) { Thread.CurrentThread.Name = "Main"; //read user input sourceFileName = @args[0].ToString(); targetFileName = @args[1].ToString(); //parallel invocation of the file copy task //you can invoke many parallel tasks this way Parallel.Invoke( () => { //start the file copy operation copyFileTPL(@sourceFileName, @targetFileName); } ); } private static void copyFileTPL(string sourceFile, string targetFile) { Console.WriteLine(); Console.WriteLine("File Copy Operation using the Task Parallel Library"); Console.WriteLine(); Console.WriteLine("File to copy: " + @sourceFile); //count file lines StreamReader sr = new StreamReader(@sourceFile); float totalLines = File.ReadLines(@sourceFile).Count(); sr.Close(); string line = ""; float progress = 0; Console.WriteLine(); Console.WriteLine("Copying file..."); int count = 0; //file copy operations using (StreamReader reader = new StreamReader(@sourceFile)) { StreamWriter writer = new StreamWriter(@targetFile); 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 = (count / totalLines) * 100; Console.WriteLine("Progress: " + Math.Round(progress, 2).ToString() + "% (rows copied: " + count.ToString() + ")"); } writer.Close(); } Console.WriteLine(); Console.WriteLine("Original File: " + @sourceFile); Console.WriteLine("New File: " + targetFile); } } }
Our code consists of two methods, that is the "copyFileTPL" method, and the "Main" method. Below, we can see in more detail what exactly these two methods do:
- copyFileTPL:
This method undertakes the file copy operation along with progress reporting.
More specifically, it performs the below:
- It uses the StreamReader class to read the source file
- It uses the StreamWriter class to write to the destination file
- Reports percentage of completion of the file copy process
- "Main":
This is the main method of our console application and actually the main thread.
It performs the below:
- It reads user input (args[0], and args[1]) that specifies the source and destination file
- It launches the file copy operation, by creating a parallel task (new thread) that calls the "copyFileTPL" method.
Let's talk a bit more about the second task that the "Main" method does, that is calling the parallel task.
TPL provides a handy method for launching parallel tasks and essentially, turning your .NET app into a multi-threaded application. This is the Parallel.Invoke method. This method, which is part of the Task Parallel Library, executes each of the provided actions (tasks), possibly in parallel. So, in our code, the usage of this method, allows the file copy operation to run in parallel with the main thread.
In our example, we called just one task using the Parallel.Invoke method. Note that you can call many parallel tasks, given of course the fact, that based on your application's logic, these tasks can be executed in parallel. The syntax to call many parallel tasks is:
Parallel.Invoke( () => { //code for parallel task 1 }, () => { //code for parallel task 2 }, () => { //code for parallel task N } );
Let's Run the Application
We run the application using the below command in command prompt:
Then, a few moments later, we see that the program completed the operation of copying the file along with reporting its progress:
Now, let's check the file that was created after executing our .NET Console App:
As you can see, the new file is just the same like the original, meaning that our application successfully performed the task it was designed to do.
Conclusion
In this tip, we examined another way of implementing multithreading in C# and more specifically, parallelism. To this end, we built a simple .NET Console App in C# that copies one file into another using the Task Parallel Library (TPL).
Multithreading and parallelism can be quite handy in software development, however, you need to take into consideration different factors when designing your application, so that your application's logic remains correct throughout the execution of parallel tasks.
When properly used, multithreading and parallelism in software development, can help you build robust software applications with enhanced performance and responsiveness, that contribute towards a much better user experience.
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: .NET Multithreading Example Using ThreadPool Class
- Check the MS Docs article: Task Parallel Library (TPL)
- Check the MS Docs article: Thread Class
- Check the MS Docs article: System.Threading Namespace
- Check the MS Docs article: System.IO Namespace
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-27