| < Day Day Up > |
We've seen the two main situations where it can be a good idea to use threading in your applications. However, there are some circumstances in which spawning a new thread would be a bad idea. Obviously, this isn't going to be a complete listing of inappropriate times to create new threads, but it is
Recall the example
do_something_thread.cs
from earlier in the chapter where we created some code demonstrating the fact that execution
Here's an example that
using System; using System.Threading; namespace Chapter 02 { public class ExecutionOrder { static Thread t1; static Thread t2; public static void WriteFinished(string threadName) { switch(threadName) { case "T1": Console.WriteLine(); Console.WriteLine("T1 Finished"); break; case "T2": Console.WriteLine(); Console.WriteLine("T2 Finished"); break; } } public static void Main() { t1 = new Thread(new ThreadStart(Increment)); t2 = new Thread(new ThreadStart(Increment)); t1.Name = "T1"; t2.Name = "T2"; t1.Start(); t2.Start(); Console.ReadLine(); } public static void Increment() { for(long i = 1; i <=1000000; i++) { if(i % 100000 == 0 ) Console.Write(" {" + Thread.CurrentThread.Name + "}"); } WriteFinished(Thread.CurrentThread.Name); } } }
Sometimes t1 will finish then t2 will execute some more code and then finish. Sometimes t2 will finish completely and then t1 will execute to completion. The point is that you can't count on the threads completing in the order they were started. Later in this book we will discuss how you can synchronize threads to execute in a specified order. However, it's important to note synchronization doesn't happen by default.
This isn't the only problem associated with execution order. Take the next piece of example code where we show that data can be adversely affected by unsynchronized threads, ExecutionOrder2:
using System; using System.Threading; public class ExecutionOrder2 { static Thread t1; static Thread t2; static int iIncr; public static void WriteFinished(string threadName) { switch(threadName) { case "T1": Console.WriteLine(); Console.WriteLine("T1 Finished: iIncr = " + iIncr.ToString); break; case "T2": Console.WriteLine(); Console.WriteLine("T2 Finished: iIncr = " + iIncr.ToString); break; } } public static void Main() { iIncr = 0; t1 = new Thread(new ThreadStart(Increment)); t2 = new Thread(new ThreadStart(Increment)); t1.Name = "T1"; t2.Name = "T2"; t1.Start(); t2.Start(); Console.Read(); } public static void Increment() { for(long i = 1; i <= 1000000; i++) { if(i % 100000 = 0) Console.WriteLine(" {" + Thread.CurrentThread.Name + "} " + iIncr.ToString()); } iIncr++; WriteFinished(Thread.CurrentThread.Name); } }
This is a very similar class to ExecutionOrder. This time, however, we created a shared incrementing counter called i Incr. We tell the application to increment the variable before moving on to the WriteFinished() method. If we execute this application a few times, you will notice that the value of the incrementing counter will change at different times. Keep in mind again that we will show you how to synchronize these threads later on. These two examples should act as warnings that threads do not execute in the order that you want by default. However, in these cases, you can use synchronization tactics such as using the Join() method we discussed earlier. Thread synchronization will be covered more in depth later in this book.
One other common mistake made when someone discovers the joys of threading is that they create and use them within a loop. There
|
|
Please be aware that running this code may well disable your system. Don't run it unless you don't mind rebooting your machine to
|
|
using System; using System.Threading; using System.Web.Mail; using System.Collections; public class LoopingThreads { public delegate void SendMail(string oMessageTo); private class MyMail { public string EmailTo; public string EmailFrom; public string EmailSubject; public string EmailBody; public SendMail SendThisEmail; // Delegate instance public void Send() { System.Web.Mail.MailMessage oMail = new System.Web.Mail.MailMessage(); oMail.To = EmailTo; oMail.From = EmailFrom; oMail.Body = EmailBody; oMail.Subject = EmailSubject; oMail.BodyFormat = MailFormat.Text; SmtpMail.Send(oMail); SendThisEmail(EmailTo); } } public static Thread CreateEmail(SendMail oSendEmail, string EmailTo , string EmailFrom , string EmailBody , string EmailSubject ) { MyMail oMail = new MyMail(); oMail.EmailFrom = EmailFrom; oMail.EmailBody = EmailBody; oMail.EmailSubject = EmailSubject; oMail.EmailTo = EmailTo; oMail.SendThisEmail = oSendEmail; Thread t = new Thread(new ThreadStart(oMail.Send)); return t; } } class Mailer { public static void MailMethod(string oString) { Console.WriteLine("Sending Email: " + oString); } } public class DoMail { static ArrayList al = new ArrayList(); public static void Main() { for(int i = 1; i <= 1000; i++) { al.Add(i.ToString() + "@someplace.com"); } SendAllEmail(); } public static void SendAllEmail() { int loopTo = al.Count - 1; for(int i = 0; i <= loopTo; i++) { Thread t = LoopingThreads.CreateEmail( new LoopingThreads.SendMail(Mailer.MailMethod), (string)al[i], "johndoe@somewhere.com", "Threading in a loop", "Mail Example"); t.Start(); t.Join(Timeout.Infinite); } } }
The code may be a little more complex than you thought because it also demonstrates how to use a delegate and a lengthy set of classes to call a thread with parameters. This is necessary because
threads can only create an entry on a method that has no parameters.
As such, it is the duty of the programmer to create proxy
Let's concentrate on the SendAllEmail method. This is where we loop through the ArrayList and send our parameters to the proxy method. We start a new thread for each and every e-mail we want to send:
public static void SendAllEmail() { int loopTo = al.Count - 1; for(int i = 0; i <= loopTo; i++) { Thread t = LoopingThreads.CreateEmail( new LoopingThreads.SendMail(Mailer.MailMethod), (string)al[i], "johndoe@somewhere.com", "Threading in a loop", "Mail Example"); t.Start(); t.Join(Timeout.Infinite); } }
At first glance, this sounds like a good idea. Why not send e-mail on another thread? It takes a long time to process sometimes doesn't it? This is true, but the problem is that we are now tying up the processor's execution time by switching between the threads. By the time this process is done, the time slice allocated to each thread is
One common programming practice is to place work into a queue to be
The file model is just one example. Another similar model may be to use Microsoft BizTalk Server or Microsoft Message Queue with a service that processes items in a queue. All of these
| < Day Day Up > |