Learn more about SQL Server tools

mssqltips logo
 

Tutorials          DBA          Dev          BI          Career          Categories          Webcasts          Scripts          Today's Tip          Join

Tutorials      DBA      Dev      BI      Categories      Webcasts

DBA    Dev    BI    Categories

 

.NET Multithreading Example Using Task Parallel Library


By:   |   Last Updated: 2019-03-27   |   Comments   |   Related Tips: More > 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.

Sample source file.

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):

Creating a .NET Console App.

Below, you can see the workspace in Visual Studio just right after the empty project is created.

.NET Console App workspace in Visual Studio.

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:

Executing our .NET Console App.

Then, a few moments later, we see that the program completed the operation of copying the file along with reporting its progress:

Our .NET Console App has just finished its execution.

Now, let's check the file that was created after executing our .NET Console App:

The generated file.

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


Last Updated: 2019-03-27


get scripts

next tip button



About the author
MSSQLTips author Artemakis Artemiou Artemakis Artemiou is a Senior SQL Server and Software Architect, Author, and a former Microsoft Data Platform MVP (2009-2018).

View all my tips




Post a comment or let the author know this tip helped.

All comments are reviewed, so stay on subject or we may delete your comment. Note: your email address is not published. Required fields are marked with an asterisk (*).

*Name    *Email    Email me updates 


Signup for our newsletter
 I agree by submitting my data to receive communications, account updates and/or special offers about SQL Server from MSSQLTips and/or its Sponsors. I have read the privacy statement and understand I may unsubscribe at any time.



    



Learn more about SQL Server tools