File Search Using Parallel Programming in C#

In this article I shall demonstrate how to do a file search using parallel programming in C#.

Before going through this article I strongly recommend you go through the basics of the Task Parallel Library and Parallel.Foreach mechanism in the .Net Framework 4.

The demo application consists of a form with 2 button controls and 2 list box controls.

Step 1: Design the form as shown.

Design the form

The purpose of this application:

  1. Load the files into list boxes.
  2. The user is able to perform the button clicks simultaneously.
  3. The user is able to close the application before the file search.
  4. Responsive UI: the user can move or resize the application during runtime.

Step 2: Create a class with the Search functionality code as shown.

  1. using System;   
  2. using System.Collections.Generic;   
  3. using System.Linq;   
  4. using System.Text;   
  5. using System.Windows.Forms;   
  6. using System.Threading.Tasks;   
  7. using System.Threading;   
  8. using System.IO;      
  9.   
  10. namespace ParallelFileSearch   
  11. {   
  12.     public class CustomFileSearch   
  13.     {   
  14.         public static TaskScheduler uiScheduler;   
  15.         public CustomFileSearch(TaskScheduler taskScheduler)   
  16.         {   
  17.             uiScheduler = taskScheduler;   
  18.         }    
  19.         static void DisplaySearchedFile(string file, ListBox listBox, Button button)   
  20.         {   
  21.             //Each file added to the listbox is a new task.   
  22.             Task.Factory.StartNew(() =>   
  23.             {   
  24.                 if (file != string.Empty)   
  25.                 {   
  26.                     listBox.Items.Add(file);    
  27.                 }   
  28.                 else   
  29.                 {   
  30.                     button.Enabled = true;   
  31.                 }   
  32.             }, CancellationToken.None, TaskCreationOptions.None, uiScheduler);    
  33.         }    
  34.         public void FileSearch(DirectoryInfo info, ListBox listBox, Button button, Action<string, ListBox, Button> currentFile)   
  35.         {   
  36.             Task.Factory.StartNew(() =>   
  37.             {   
  38.                 // During every button click the listbox is cleared and the button is disabled.   
  39.                 listBox.Items.Clear();   
  40.                 button.Enabled = false;   
  41.             }, CancellationToken.None, TaskCreationOptions.None, uiScheduler);   
  42.     
  43.             IEnumerable<FileInfo> fileInfo = info.EnumerateFiles("*.cs", SearchOption.AllDirectories);   
  44.             Parallel.ForEach(fileInfo, s =>   
  45.             {    
  46.                 Thread.Sleep(300);   
  47.                 currentFile(s.FullName, listBox, button);    
  48.             });   
  49.             // This is to perform the button enable/disable functionality.   
  50.             //Once the search is done the clicked button is enabled.   
  51.             currentFile(string.Empty, listBox, button);   
  52.         }   
  53.     }   
  54. }  

CustomFileSearch class consists of the following 2 methods.

  1. FileSearch
  2. DisplaySearchedFile

Let's discuss the FileSearch method. It consists of the following 4 parameters:

  1. DirectoryInfo info: This accepts the directory for file search. It is supplied from the client side.
  2. ListBox listBox: This accepts the listbox from the client side. This is used in the method DisplaySearchedFile.
  3. Button button: This accepts the button from the client side. This is used in the method DisplaySearchedFile.
  4. Action<string, ListBox, Button> currentFile: This is an action delegate that accepts parameters of string type, Listbox type and Button type. 
    1. Task.Factory.StartNew(() =>  
    2. {  
    3.      // During every button click the listbox is cleared and the button is disabled.  
    4.      listBox.Items.Clear();  
    5.     button.Enabled = false;  
    6. },  CancellationToken.None, TaskCreationOptions.None, uiScheduler); 

The button enable/disable and list box clearing functionality is done here. It is done in a separate task so that the main UI thread is independent of these functionalities. This is done by passing a Task Scheduler object to the Task.

  1.  IEnumerable<FileInfo> fileInfo = info.EnumerateFiles("*.cs", SearchOption.AllDirectories);  
  2. Parallel.ForEach(fileInfo, s =>  
  3. {  
  4.      Thread.Sleep(300);                
  5.       currentFile(s.FullName, listBox, button);   
  6. });  
  7.   
  8. // This is to perform the button enable/disable functionality.  
  9. //Once the search is done the clicked button is enabled.  
  10. currentFile(string.Empty, listBox, button); 

First we get the information of all files ending with .cs extension as a FileInfo collection. Then the Parallel.ForEach is run on this collection. For each selected file the delegate is invoked with the current file name, listbox and button as parameters. This delegate invokes the method DisplaySearchedFile.

DisplaySearchedFile

  1. static void DisplaySearchedFile(string file, ListBox listBox, Button button)  
  2. {  
  3.      //Each file added to the listbox is a new task.          
  4.      Task.Factory.StartNew(() =>  
  5.      {  
  6.          if (file != string.Empty)  
  7.          {  
  8.              listBox.Items.Add(file);   
  9.         }  
  10.         else  
  11.         {  
  12.               button.Enabled = true;  
  13.          }  
  14.     }, CancellationToken.None, TaskCreationOptions.None, uiScheduler);  
  15.  } 

It consists of the following 3 parameters:

  1. string file: Searched file
  2. ListBox listBox
  3. Button button

Each searched file is added to the list box as a separate task. This is to ensure that the processes that are running are not affected (synchronize the main thread and the current thread.). This is done by passing the Task Scheduler object to the Task.

Step 3: Main Form:

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.ComponentModel;  
  4. using System.Data;  
  5. using System.Drawing;  
  6. using System.Linq;  
  7. using System.Text;  
  8. using System.Windows.Forms;  
  9. using System.IO;  
  10. using System.Threading.Tasks;  
  11. using System.Threading;  
  12. using System.Diagnostics;  
  13. using System.Collections;  
  14.   
  15. namespace ParallelFileSearch  
  16. {  
  17.     public partial class ParallelSearch : Form  
  18.     {  
  19.         TaskScheduler uiScheduler;  
  20.         CustomFileSearch customFileSearch;  
  21.         public ParallelSearch()  
  22.         {  
  23.             InitializeComponent();  
  24.         }  
  25.   
  26.         private void Form1_Load(object sender, EventArgs e)  
  27.         {  
  28.             uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();  
  29.             customFileSearch = new CustomFileSearch(uiScheduler);  
  30.         }   
  31.         private void btnDrive1_Click(object sender, EventArgs e)  
  32.         {  
  33.             DirectoryInfo dirInfo = new DirectoryInfo(@"C:\ ");  
  34.             Task.Factory.StartNew(() => customFileSearch.FileSearch(dirInfo, lstFile1, btnDrive1, CustomFileSearch.DisplaySearchedFile));  
  35.         }  
  36.         private void btnDrive2_click(object sender, EventArgs e)  
  37.         {  
  38.             DirectoryInfo dirInfo = new DirectoryInfo(@"D:\ ");  
  39.             Task.Factory.StartNew(() => customFileSearch.FileSearch(dirInfo, lstFile2, btnDrive2, CustomFileSearch.DisplaySearchedFile));  
  40.         }  
  41.     }  

In the form load event we have passed the Task Scheduler object to the constructor of the CustomFileSearch class.

In the button click event I have hard-coded the drives as C drive and D drive. My machine consists of only C and D drives. You can change it to your own machine's drives.

  1. DirectoryInfo dirInfo = new DirectoryInfo(@"C:\ ");  
  2. DirectoryInfo dirInfo = new DirectoryInfo(@"D:\ "); 

Inside the button click we call the FileSearch method with the respective parameters as shown.

  1. Task.Factory.StartNew(() => customFileSearch.FileSearch(dirInfo, lstFile1, btnDrive1, CustomFileSearch.DisplaySearchedFile));    
  2. Task.Factory.StartNew(() => customFileSearch.FileSearch(dirInfo, lstFile2, btnDrive2,   CustomFileSearch.DisplaySearchedFile)); 

Output

Output

Before running the app, ensure that you have the privilege to read specific files in your computer via this application. Since I am having some privilege issues (company PC) I have used the same drive for the search functionality.

For a better understanding I have shared the source code. Before running the application, ensure that you have the current drives hard-coded in the button clicks. If not please change it to your computer-specific drives.

Just try to resize or move the application while running.