Threading Opportunities

 < Day Day Up > 



So, now I may have you wondering why we would thread at all if it could potentially have a negative impact on our application. The idea is that you will learn there is a time and place for threading. Learning which circumstances represent good opportunities to spawn a new thread is the key to making good design decisions. There are two distinct opportunities for which to consider spawning a new thread. In this section, we will discuss what those opportunities are.

Background Processes

The first opportunity to spawn a new thread occurs when your application needs to run a large process in the background while still keeping its user interface active and usable. We have all run into times when an application just didn't seem to respond because we had told it to query data or process a large piece of information. Take, for example, the case of professional graphics packages that are required to render graphics into a specific file format. In early versions of some products, asking the application to render a large graphic would result in the application becoming unresponsive until the rendering process had finished. You'd often have to finish working with the application, then set it to render overnight - coming back in the morning to see if the results were what you expected - because sitting and waiting in front of the computer for an hour was just not viable. This problem presents an ideal time to set a background thread to do your computer-intensive processing while leaving your user interface to run on the main application thread.

Let's take a look at an example of a background process that needs to spawn a new thread. This example demonstrates searching for files. When the search routine finds a file matching the pattern specified, it adds a new item to the ListBox.

click to expand

The code below will demonstrate that this method does indeed need its own thread:

    using System.Threading;    using System.IO;    public class Threaded Search : System.Windows.Forms.Form    {      string search;      int fileCount;      private void cmdSingle Click(object sender, System.EventArgs e)      {        Search();      }      public void Search()      {        search = TextBox1.Text;        ListBox1.Items.Clear();        fileCount = 0;        SearchDirectory(@"C:\winnt");      }      public void SearchDirectory(string Path)      {        // Search the directory        DirectoryInfo di = new DirectoryInfo(Path);        FileInfo[] f = di.GetFiles(search);        ListBox1.BeginUpdate();        foreach(FileInfo myFile in f)          ListBox1.Items.Add(myFile.FullName);        ListBox1.EndUpdate();        // Search its sub directories        DirectoryInfo[] d = di.GetDirectories();        foreach(DirectoryInfo myDir in d)          SearchDirectory(myDir.FullName);      }    } 

Go ahead and compile this example and run it. Type a search term in the search textbox, such as * . *, click the Single Thread Search button, and observe our problem. As you will see, we are searching for files and trying to update the user interface every time we find a file with our search criteria. However, because both the user interface and the search code are running on the same thread, we don't see the updates until the search code has completely finished its processing. Additionally, we cannot resize our form while the search is processing.

This rather long piece of code is actually a very simple demonstration. Let's see if we can correct this problem with a simple change. In the Button2_Click routine, add the following code to call the Search() method with the use of a new thread:

    private void cmdMulti Click(object sender, System.EventArgs e)    {      Thread t = new Thread(new ThreadStart(Search));      t.Start();    } 

Now recompile and run the program again. This time, type in the same search term and click the Multi Thread Search button. You can see that there is quite a difference. This time our results are displayed immediately. This is because Windows is now switching execution back and forth between the user interface and the search thread. The processor is now given a time slice to update the interface to reflect the changes in the ListBox. You will also notice that we can now resize our form while it is searching.

There are other background processes that may cause our interface to be unresponsive. We might want to do some intense processing, such as searching, sorting, formatting, parsing, and filtering a large number of records in a database. This would be another opportunity to consider using a new thread. You may also want to spawn a new thread if you want to run a routine that is constantly logging information. The user interface won't necessarily be unresponsive in this instance, but it may appear slow or sluggish if this type of routine isn't on its own thread.

Accessing External Resources

The second circumstance in which you might want to consider spawning a new thread occurs when you are accessing resources that are not local to your system. This might be a database process or a network file share somewhere on your network. In such cases, network performance could adversely affect your application performance.

Let's take the following example. We are going to connect to a database in this example. Let's assume that network performance is poor and may cause this application to be slow. Let's also assume that company policy dictates that no applications can be installed on the database server:

    using System.Threading;    using System.IO;    using System.Data;    using System.Data.SqlClient;    namespace Chapter 02    {      public class Threaded Resource : System.Windows.Forms.Form      {        public void Button1 Click(object sender , System.EventArgs e)        {          QueryData();        }        public void QueryData()        {          SqlDataReader objReader;          SqlConnection objConn;          SqlCommand objCommand;          int intEmployeeID;          string strFirstName;          string strTitle;          int intReportsTo;          objConn = new SqlConnection("server=RemoteServer;" +              "UID=RemoteUser;PWD=Password;database=northwind");          objCommand = new SqlCommand("SELECT EmployeeID, FirstName, " +              "Title, ReportsTo FROM Employees", objConn);          objConn.Open();          objReader = objCommand.ExecuteReader(              CommandBehavior.CloseConnection );          while (objReader.Read())          {            intEmployeeID = objReader.GetInt32(0);            strFirstName = objReader.GetString(1);            strTitle = objReader.GetString(2);            if(objReader.IsDBNull(3))              intReportsTo = 0;            else              intReportsTo = objReader.GetInt32(3);            listBox1.Items.Add(intEmployeeID.ToString() + " " +                strFirstName + " " + strTitle + " " +                intReportsTo.ToString());          }          objReader.Close();          objConn.Close();        }        public static void Main()        {          Application.Run(new Threaded_Resource());        }      }    } 

As you can see in this example, all we are doing is querying a remote database. The data returned will not be excessive, but you will notice that the user interface freezes while it takes time to get the data and update the listbox. We can again correct this by simply spawning a new thread and executing our database code within that thread. Let's add a second button and use the following code:

      private void button2 Click(object sender, System.EventArgs e)      {        Thread t = new Thread(new ThreadStart(QueryData));        t.Start();      } 

Now when we run the code, we get a result similar to our last example. We can resize the form while the query runs. The interface is responsive throughout the entire query process.

Of course, I want to reiterate that this doesn't necessarily mean you should spawn a new thread every time you connect to a database. However, analyze your options to find out if you can move the database or the application so they reside on the same server. Also, make sure that this component isn't going to be continuously called from various client applications. Doing so would spawn additional threads for every call and consume more resources than you intended. There are ways to reuse objects and their threads without using a new thread every time your object is called. These issues will be covered in Chapters 3 and 5.



 < Day Day Up > 



C# Threading Handbook
C# Threading Handbook
ISBN: 1861008295
EAN: 2147483647
Year: 2003
Pages: 74

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net