The Single-Threaded Example
As you become more familiar with multithreading, you will be able to start working directly on an application design that uses this technique. As a start, however, I recommend designing the process as if it will occur all on a single thread. When you have a system working in a single-threaded or synchronous model, you can then make the necessary changes to turn it into a system that uses background processing. I will follow this path myself in this article, building an application without any background work and then converting that as required.
For the purposes of this article, I will be using the previous example, an application that needs to scan the hard drive for music files and load up a list view with its findings. So, without going through any proper design process (although you can pretend that I did, if you would like), I'll jump right into coding the single-threaded version of this file scanner. Now, the way I like to develop, whether I am working from a design or just writing a quick example like this, is to create the important functionality first, without complicating the issue by worrying about the user interface. In this case, that means I am going to write a file-scanning procedure that works but that is not necessarily the final version that will be used in conjunction with my Form. The module in Listing 1 (which could be compiled and used from the command line, if you want) illustrates the simple file-scan code on its own. As each file is found, it is added into a global ArrayList instance, which would contain the complete results of the scan at the end of the program.
Listing 1: Prototype File Scanner
Imports System Imports System.IO Module FileScanner Dim WMAFiles As New ArrayList() Sub Main() StartScan("*.wma") Console.WriteLine("{0} Files Found", WMAFiles.Count) Console.ReadLine() End Sub Private Sub StartScan(ByVal filter As String) Dim directoriesToSearch As String() Dim currentDirectory As String directoriesToSearch = Directory.GetLogicalDrives() For Each currentDirectory In directoriesToSearch SearchForFiles(currentDirectory, True, filter) Next End Sub Private Sub SearchForFiles(ByVal dir As String, _ ByVal subdirectories As Boolean, _ ByVal filter As String) Dim foundFiles As String() Dim foundFile As String Try foundFiles = Directory.GetFiles(dir, filter) For Each foundFile In foundFiles WMAFiles.Add(foundFile) Next If subdirectories Then Dim foundDirs As String() Dim currentDir As String foundDirs = Directory.GetDirectories(dir) For Each currentDir In foundDirs SearchForFiles(currentDir, _ subdirectories, filter) Next End If Catch 'If I'm not allowed access 'to a file or folder due to 'security or a missing volume, 'then I want to just ignore it. End Try End Sub End Module
To convert this code for use with a Windows Forms interface, you could start the scan in the Load event of the Form and then bind or copy the contents of the ArrayList to a user interface element such as a ListBox. You would require the same two procedures, StartScan and SearchForFiles, and a global instance of the ArrayList, as in Listing 1, but the code in the Load event of the Form would look like this:
Dim WMAFiles As New ArrayList() Private Sub frmWMAList_Load(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles MyBase.Load StartScan("*.wma") lbFiles.DataSource = WMAFiles End Sub
The end result of this first version of the code will be that the scan occurs before the Form is ever displayed, causing a delay in the application startup. Converting this code into a multithreaded version is the best way to minimize that startup delay, presenting the user with an interface as quickly as possible.