static void Main(string[] args) { // Get the currently-running thread object. Thread primaryThreadObject = Thread.CurrentThread; // Set the name of the thread. This will help with debugging // when looking at the Threads window. primaryThreadObject.Name = "The Main Thread"; // Get the thread ID so that we can use it in output statements. int threadId = primaryThreadObject.ManagedThreadId; try { // Display a message to show we're in Main(). Console.WriteLine("{0}: Starting the program.", threadId); // Get the number of milliseconds from the arguments // passed in from the command line. int milliseconds = GetMilliseconds(args[0]); // Create the ComplicatedCalculator object. ComplicatedCalculator cc = new ComplicatedCalculator(milliseconds); // Create the ParameterizedThreadStart delegate. This // delegate will be used to pass an array of doubles // to the method on the secondary thread. double[] numbers = { 10.4, 7.451 }; ParameterizedThreadStart threadedMethod = new ParameterizedThreadStart(cc.CalculateValue); // Create the thread object and start the thread. In this // case, when we call Start(), we pass in the double array // as an argument. Thread secondaryThread = new Thread(threadedMethod); // Set the name of the secondary thread. secondaryThread.Name = "Calculation Thread"; // Start the thread. secondaryThread.Start(numbers); // Display some messages to show that Main() is still // responsive while the calculation is going on. Console.WriteLine ("\n{0}: Now I'm going to go do something else.", threadId); System.Threading.Thread.Sleep(1500); Console.WriteLine("\n{0}: Like talk about the weather.", threadId); System.Threading.Thread.Sleep(1500); Console.WriteLine("\n{0}: Or the latest news.", threadId); System.Threading.Thread.Sleep(1500); Console.WriteLine("\n{0}: You know, my foot hurts.", threadId); System.Threading.Thread.Sleep(1500); Console.WriteLine("\n{0}: I love hotdogs!", threadId); System.Threading.Thread.Sleep(1500); Console.WriteLine ("\n{0}: How much is a shake at Burgermaster?", threadId); System.Threading.Thread.Sleep(1500); Console.WriteLine("\n{0}: Ok, now I'm getting hungry!", threadId); System.Threading.Thread.Sleep(1500); // Join the secondary thread, but don't wait forever. Console.WriteLine("\n{0}: Joining secondary thread {1}: {2}.", threadId, secondaryThread.ManagedThreadId, secondaryThread.Name); if (!secondaryThread.Join(10000)) { Console.WriteLine ("\n{0}: Thread {1}: {2} " + "is still alive. Calling Abort().", threadId, secondaryThread.ManagedThreadId, secondaryThread.Name); // If it is, abort the thread and then Join it again. secondaryThread.Abort(); if (!secondaryThread.Join(10000)) { Console.WriteLine ("\n{0}: Thread {1}: {2} is still running!", threadId, secondaryThread.ManagedThreadId, secondaryThread.Name); } } Console.WriteLine("\n{0}: The result is: {1}", threadId, cc.Results); } catch (Exception e) { Console.WriteLine("\n{0}: EXCEPTION: {1}.", threadId, e.Message); } // Pause so we can look at the console window. Console.Write("\n\n{0}: Press <ENTER> to end: ", threadId); Console.ReadLine(); }
static void Main(string[] args) { try { // Display a message to show we're in Main(). Console.WriteLine("Starting the program."); // Get the number of milliseconds from the arguments // passed in from the command line. int milliseconds = GetMilliseconds(args[0]); // Create the ComplicatedCalculator object. ComplicatedCalculator cc = new ComplicatedCalculator(milliseconds); // Create the ParameterizedThreadStart delegate. This // delegate will be used to pass an array of doubles // to the method on the secondary thread. double[] numbers = { 10.4, 7.451 }; ParameterizedThreadStart threadedMethod = new ParameterizedThreadStart(cc.CalculateValue); // Create the thread object and start the thread. In this // case, when we call Start(), we pass in the double array // as an argument. Thread secondaryThread = new Thread(threadedMethod); secondaryThread.Start(numbers); // Display some messages to show that Main() is still // responsive while the calculation is going on. Console.WriteLine ("\nNow I'm going to go do something else."); System.Threading.Thread.Sleep(1500); Console.WriteLine("\nLike talk about the weather."); System.Threading.Thread.Sleep(1500); Console.WriteLine("\nOr the latest news."); System.Threading.Thread.Sleep(1500); Console.WriteLine("\nYou know, my foot hurts."); System.Threading.Thread.Sleep(1500); Console.WriteLine("\nI love hotdogs!"); System.Threading.Thread.Sleep(1500); Console.WriteLine("\nHow much is a shake at Burgermaster?"); System.Threading.Thread.Sleep(1500); Console.WriteLine("\nOk, now I'm getting hungry!"); System.Threading.Thread.Sleep(1500); // How do we know that we have the answer? At this point // we don't. We'll take a chance for now and hope that the // answer is there. If the argument passed into Main() is // set to 2500, then this will work. If it's set to 7500, // then the result will be 0. Console.WriteLine("\n\tThe result is: {0}", cc.Results); } catch (Exception e) { Console.WriteLine("\nEXCEPTION: {0}.", e.Message); } // Pause so we can look at the console window. Console.Write("\n\nPress <ENTER> to end: "); Console.ReadLine(); }
static void Main(string[] args) { // Get the currently-running thread object. Thread primaryThreadObject = Thread.CurrentThread; // Set the name of the thread. This will help with debugging // when looking at the Threads window. primaryThreadObject.Name = "The Main Thread"; // Get the thread ID so that we can use it in output statements. int threadId = primaryThreadObject.ManagedThreadId; try { // Display a message to show we're in Main(). Console.WriteLine("{0}: Starting the program.", threadId); // Get the number of milliseconds from the arguments // passed in from the command line. int milliseconds = GetMilliseconds(args[0]); // Create the ComplicatedCalculator object. ComplicatedCalculator cc = new ComplicatedCalculator (milliseconds, primaryThreadObject); // Create the ParameterizedThreadStart delegate. This // delegate will be used to pass an array of doubles // to the method on the secondary thread. double[] numbers = { 10.4, 7.451 }; ParameterizedThreadStart threadedMethod = new ParameterizedThreadStart(cc.CalculateValue); // Create the thread object and start the thread. In this // case, when we call Start(), we pass in the double array // as an argument. Thread secondaryThread = new Thread(threadedMethod); // Set the name of the secondary thread. secondaryThread.Name = "Calculation Thread"; // Start the thread. secondaryThread.Start(numbers); // Display some messages to show that Main() is still // responsive while the calculation is going on. Console.WriteLine ("\n{0}: Now I'm going to go do something else.", threadId); System.Threading.Thread.Sleep(1500); Console.WriteLine("\n{0}: Like talk about the weather.", threadId); System.Threading.Thread.Sleep(1500); Console.WriteLine("\n{0}: Or the latest news.", threadId); System.Threading.Thread.Sleep(1500); Console.WriteLine("\n{0}: You know, my foot hurts.", threadId); System.Threading.Thread.Sleep(1500); Console.WriteLine("\n{0}: I love hotdogs!", threadId); System.Threading.Thread.Sleep(1500); Console.WriteLine ("\n{0}: How much is a shake at Burgermaster?", threadId); System.Threading.Thread.Sleep(1500); Console.WriteLine("\n{0}: Ok, now I'm getting hungry!", threadId); System.Threading.Thread.Sleep(1500); // Sleep forever. We're really hoping that the worker thread // wakes us up! This needs to be wrapped in it's own try{} // block to catch the exception that is caused when the // Interrupt() method is called. try { Console.WriteLine ("\n{0}: Main thread going to sleep now.", threadId); System.Threading.Thread.Sleep(Timeout.Infinite); } catch (ThreadInterruptedException) { // Ignore this exception since we're expecting it. } Console.WriteLine("\n\t{0}: The result is: {1}", threadId, cc.Results); } catch (Exception e) { Console.WriteLine("\n{0}: EXCEPTION: {1}.", threadId, e.Message); } // Pause so we can look at the console window. Console.Write("\n\n{0}: Press <ENTER> to end: ", threadId); Console.ReadLine(); }
static void Main(string[] args) { // Get the currently-running thread object. Thread primaryThreadObject = Thread.CurrentThread; // Set the name of the thread. This will help with debugging // when looking at the Threads window. primaryThreadObject.Name = "The Main Thread"; // Get the thread ID so that we can use it in output statements. int threadId = primaryThreadObject.ManagedThreadId; // Set the original color of the color. ConsoleColorObject.SetConsoleColor(); try { // Display a message to show we're in Main(). Console.WriteLine("{0}: Starting the program.", threadId); // Get the number of milliseconds from the arguments // passed in from the command line. int milliseconds = GetMilliseconds(args[0]); // Declare an array of ComplicatedCalculator objects ComplicatedCalculator[] ccList = new ComplicatedCalculator[MAX_OPERATIONS]; // Populate the array with object. for (int i = 0; i < ccList.Length; i++) { // Pass an instance of the callback delegate defined above // to the asynchronous type. CallbackDelegate cd = new CallbackDelegate(Program.ThreadDoneCallback); ComplicatedCalculator cc = new ComplicatedCalculator(milliseconds, cd); // Store several instances of the asynchronous type into // an array. ccList[i] = cc; } // Create a couple of number generators for the data. Random firstNum = new Random(DateTime.Now.Millisecond); Random secondNum = new Random(DateTime.Now.Minute); // Start up a set of foreground threads for each instance of the // asynchronous type stored in the array. for (int i = 0; i < ccList.Length; i++) { double[] numbers = { firstNum.Next(1, 30), secondNum.Next(1, 7) }; ParameterizedThreadStart threadedMethod = new ParameterizedThreadStart(ccList[i].CalculateValue); // Create the thread object and start the thread. In this // case, when we call Start(), we pass in the double array // as an argument. Thread secondaryThread = new Thread(threadedMethod); // Set the name of the secondary thread. secondaryThread.Name = "Calculation Thread #" + i.ToString(); // Start the threads. secondaryThread.Start(numbers); } // Notice now that each Console.WriteLine() call is now // in a critical section. This is here to synchronize with // the threads when they are writing their output in a // different color. lock (SyncObject.Sync) { Console.WriteLine ("\n{0}: Now I'm going to go do something else.", threadId); } System.Threading.Thread.Sleep(1500); lock (SyncObject.Sync) { Console.WriteLine("\n{0}: Like talk about the weather.", threadId); } System.Threading.Thread.Sleep(1500); lock (SyncObject.Sync) { Console.WriteLine("\n{0}: Or the latest news.", threadId); } System.Threading.Thread.Sleep(1500); lock (SyncObject.Sync) { Console.WriteLine("\n{0}: You know, my foot hurts.", threadId); } System.Threading.Thread.Sleep(1500); lock (SyncObject.Sync) { Console.WriteLine("\n{0}: I love hotdogs!", threadId); } System.Threading.Thread.Sleep(1500); lock (SyncObject.Sync) { Console.WriteLine ("\n{0}: How much is a shake at Burgermaster?", threadId); } System.Threading.Thread.Sleep(1500); lock (SyncObject.Sync) { Console.WriteLine("\n{0}: Ok, now I'm getting hungry!", threadId); } System.Threading.Thread.Sleep(1500); lock (SyncObject.Sync) { Console.WriteLine ("\n{0}: Waiting for threads to complete.", threadId); } // Put the primary thread into a wait state. This // will wait until the AutoResetEvent object is // signalled through a call to Set(). signalPrimaryThread.WaitOne(); // Now display the results of all the threads. for (int i = 0; i < ccList.Length; i++) { Console.WriteLine("\n{0}: {1} raised to {2} = {3}", threadId, ccList[i].FirstNumber, ccList[i].SecondNumber, ccList[i].Results); } } catch (Exception e) { Console.WriteLine("\n{0}: EXCEPTION: {1}.", threadId, e.Message); } // Pause so we can look at the console window. Console.Write("\n\n{0}: Press <ENTER> to end: ", threadId); Console.ReadLine(); }
static void Main(string[] args) { // Get the currently-running thread object. Thread primaryThreadObject = Thread.CurrentThread; // Set the name of the thread. This will help with debugging // when looking at the Threads window. primaryThreadObject.Name = "The Main Thread"; // Get the thread ID so that we can use it in output statements. int threadId = primaryThreadObject.ManagedThreadId; try { // Display a message to show we're in Main(). Console.WriteLine("{0}: Starting the program.", threadId); // Get the number of milliseconds from the arguments // passed in from the command line. int milliseconds = GetMilliseconds(args[0]); // Create the ComplicatedCalculator objects. ComplicatedCalculator cc1 = new ComplicatedCalculator(milliseconds); ComplicatedCalculator cc2 = new ComplicatedCalculator(milliseconds); // Create the ParameterizedThreadStart delegate. This // delegate will be used to pass an array of doubles // to the method on the secondary thread. double[] numbers1 = { 10.4, 7.451 }; double[] numbers2 = { 18.7, 3.6 }; ParameterizedThreadStart threadedMethod1 = new ParameterizedThreadStart(cc1.CalculateValue); ParameterizedThreadStart threadedMethod2 = new ParameterizedThreadStart(cc2.CalculateValue); // Create the thread objects and start the threads. In this // case, when we call Start(), we pass in the double array // as an argument. Thread secondaryThread1 = new Thread(threadedMethod1); Thread secondaryThread2 = new Thread(threadedMethod2); // Set the name of the secondary threads. secondaryThread1.Name = "Calculation Thread #1"; secondaryThread2.Name = "Calculation Thread #2"; // Start the threads. secondaryThread1.Start(numbers1); secondaryThread2.Start(numbers2); // Notice now that each Console.WriteLine() call is now // in a critical section. This is here to synchronize with // the threads when they are writing their output in a // different color. if (Monitor.TryEnter(SyncObject.Sync, 2000)) { try { LockingThread = Thread.CurrentThread; Console.WriteLine ("\n{0}: Now I'm going to go do something else.", threadId); } finally { Program.LockingThread = null; Monitor.Exit(SyncObject.Sync); } } System.Threading.Thread.Sleep(1500); if (Monitor.TryEnter(SyncObject.Sync, 2000)) { try { LockingThread = Thread.CurrentThread; Console.WriteLine("\n{0}: Like talk about the weather.", threadId); } finally { Program.LockingThread = null; Monitor.Exit(SyncObject.Sync); } } System.Threading.Thread.Sleep(1500); if (Monitor.TryEnter(SyncObject.Sync, 2000)) { try { LockingThread = Thread.CurrentThread; Console .WriteLine("\n{0}: Or the latest news.", threadId); } finally { Program.LockingThread = null; Monitor.Exit(SyncObject.Sync); } } System.Threading.Thread.Sleep(1500); if (Monitor.TryEnter(SyncObject.Sync, 2000)) { try { LockingThread = Thread.CurrentThread; Console.WriteLine("\n{0}: You know, my foot hurts.", threadId); } finally { Program.LockingThread = null; Monitor.Exit(SyncObject.Sync); } } System.Threading.Thread.Sleep(1500); if (Monitor.TryEnter(SyncObject.Sync, 2000)) { try { LockingThread = Thread.CurrentThread; Console.WriteLine("\n{0}: I love hotdogs!", threadId); } finally { Program.LockingThread = null; Monitor.Exit(SyncObject.Sync); } } System.Threading.Thread.Sleep(1500); if (Monitor.TryEnter(SyncObject.Sync, 2000)) { try { LockingThread = Thread.CurrentThread; Console.WriteLine ("\n{0}: How much is a shake at Burgermaster?", threadId); } finally { Program.LockingThread = null; Monitor.Exit(SyncObject.Sync); } } System.Threading.Thread.Sleep(1500); if (Monitor.TryEnter(SyncObject.Sync, 2000)) { try { LockingThread = Thread.CurrentThread; Console.WriteLine("\n{0}: Ok, now I'm getting hungry!", threadId); } finally { Program.LockingThread = null; Monitor.Exit(SyncObject.Sync); } } System.Threading.Thread.Sleep(1500); if (Program.LockingThread != null) { JoinThread(threadId, LockingThread); } // Join one of the secondary threads. JoinThread(threadId, secondaryThread1); // Join the other secondary thread. JoinThread(threadId, secondaryThread2); // We don't need to synchronize here because the threads // should already be done. Console.WriteLine("\n{0}: The result from {1} is: {2}", threadId, secondaryThread1.ManagedThreadId, cc1.Results); Console.WriteLine("\n{0}: The result from {1} is: {2}", threadId, secondaryThread2.ManagedThreadId, cc2.Results); } catch (Exception e) { Console.WriteLine("\n{0}: EXCEPTION: {1}.", threadId, e.Message); } // Pause so we can look at the console window. Console.Write("\n\n{0}: Press <ENTER> to end: ", threadId); Console.ReadLine(); }
static void Main(string[] args) { // Get the currently-running thread object. Thread primaryThreadObject = Thread.CurrentThread; // Set the name of the thread. This will help with debugging // when looking at the Threads window. primaryThreadObject.Name = "The Main Thread"; // Get the thread ID so that we can use it in output statements. int threadId = primaryThreadObject.ManagedThreadId; try { // Display a message to show we're in Main(). Console.WriteLine("{0}: Starting the program.", threadId); // Get the number of milliseconds from the arguments // passed in from the command line. int milliseconds = GetMilliseconds(args[0]); // Create the ComplicatedCalculator objects. ComplicatedCalculator cc1 = new ComplicatedCalculator(milliseconds); ComplicatedCalculator cc2 = new ComplicatedCalculator(milliseconds); ComplicatedCalculator cc3 = new ComplicatedCalculator(milliseconds); ComplicatedCalculator cc4 = new ComplicatedCalculator(milliseconds); // Create the ParameterizedThreadStart delegate. This // delegate will be used to pass an array of doubles // to the method on the secondary thread. // First run using the set of arguments below. double[] numbers1 = { 10.4, 7.451 }; double[] numbers2 = { 18.7, 3.6 }; double[] numbers3 = { 12.7, 8.6 }; double[] numbers4 = { 15.2, 5.3 }; // Second run with set of arguments below showing that the // results are no different whether or not lambda // expressions are used. //double[] numbers1 = { 10.4, 7.451 }; //double[] numbers2 = { 10.4, 7.451 }; //double[] numbers3 = { 10.4, 7.451 }; //double[] numbers4 = { 10.4, 7.451 }; ParameterizedThreadStart threadedMethod1 = new ParameterizedThreadStart(cc1.CalculateValue); // Create the thread objects and start the threads. In this // case, when we call Start(), we pass in the double array // as an argument. // All 4 ways of creating a thread instance below, causes the // ParameterizedThreadStart delegate to be generated by the compiler. // Creates an instance of a thread that is assigned a // ParameterizedThreadStart delegate. Thread secondaryThread1 = new Thread(threadedMethod1); // Creates a new thread that is implicitly assigned a ParameterizedThreadStart // delegate for the new thread. Thread secondaryThread2 = new Thread(cc2.CalculateValue); // Use a lambda expression to create a new thread that is implicitly assigned a // ParameterizedThreadStart delegate. Thread secondaryThread3 = new Thread((Object obj) => cc3.CalculateValue(obj)); // Use a lambda expression to create a new thread that is implicitly assigned a // ParameterizedThreadStart delegate. Thread secondaryThread4 = new Thread((obj) => { cc4.CalculateValue(obj); }); // Set the name of the secondary threads. secondaryThread1.Name = "Calculation Thread #1"; secondaryThread2.Name = "Calculation Thread #2"; secondaryThread3.Name = "Calculation Thread #3"; secondaryThread4.Name = "Calculation Thread #4"; // Start the threads. secondaryThread1.Start(numbers1); secondaryThread2.Start(numbers2); secondaryThread3.Start(numbers3); secondaryThread4.Start(numbers4); // Display some messages to show that Main() is still // responsive while the calculation is going on. Console.WriteLine ("\n{0}: Now I'm going to go do something else.", threadId); System.Threading.Thread.Sleep(1500); Console.WriteLine("\n{0}: Like talk about the weather.", threadId); System.Threading.Thread.Sleep(1500); Console.WriteLine("\n{0}: Or the latest news.", threadId); System.Threading.Thread.Sleep(1500); Console.WriteLine("\n{0}: You know, my foot hurts.", threadId); System.Threading.Thread.Sleep(1500); Console.WriteLine("\n{0}: I love hotdogs!", threadId); System.Threading.Thread.Sleep(1500); Console.WriteLine ("\n{0}: How much is a shake at Burgermaster?", threadId); System.Threading.Thread.Sleep(1500); Console.WriteLine("\n{0}: Ok, now I'm getting hungry!", threadId); System.Threading.Thread.Sleep(1500); // Join one of the secondary threads. JoinThread(threadId, secondaryThread1); // Join the other secondary thread. JoinThread(threadId, secondaryThread2); // Join the other secondary thread. JoinThread(threadId, secondaryThread3); // Join the other secondary thread. JoinThread(threadId, secondaryThread4); Console.WriteLine("\n{0}: The result from {1} is: {2}", threadId, secondaryThread1.ManagedThreadId, cc1.Results); Console.WriteLine("\n{0}: The result from {1} is: {2}", threadId, secondaryThread2.ManagedThreadId, cc2.Results); Console.WriteLine("\n{0}: The result from {1} is: {2}", threadId, secondaryThread3.ManagedThreadId, cc3.Results); Console.WriteLine("\n{0}: The result from {1} is: {2}", threadId, secondaryThread4.ManagedThreadId, cc4.Results); } catch (Exception e) { Console.WriteLine("\n{0}: EXCEPTION: {1}.", threadId, e.Message); } // Pause so we can look at the console window. Console.Write("\n\n{0}: Press <ENTER> to end: ", threadId); Console.ReadLine(); }
static void Main(string[] args) { // Get the currently-running thread object. Thread primaryThreadObject = Thread.CurrentThread; // Set the name of the thread. This will help with debugging // when looking at the Threads window. primaryThreadObject.Name = "The Main Thread"; // Get the thread ID so that we can use it in output statements. int threadId = primaryThreadObject.ManagedThreadId; try { // Display a message to show we're in Main(). Console.WriteLine("{0}: Starting the program.", threadId); // Get the number of milliseconds from the arguments // passed in from the command line. int milliseconds = GetMilliseconds(args[0]); // Create the ComplicatedCalculator objects. ComplicatedCalculator cc1 = new ComplicatedCalculator(milliseconds); ComplicatedCalculator cc2 = new ComplicatedCalculator(milliseconds); // Create the ParameterizedThreadStart delegate. This // delegate will be used to pass an array of doubles // to the method on the secondary thread. double[] numbers1 = { 10.4, 7.451 }; double[] numbers2 = { 18.7, 3.6 }; ParameterizedThreadStart threadedMethod1 = new ParameterizedThreadStart(cc1.CalculateValue); ParameterizedThreadStart threadedMethod2 = new ParameterizedThreadStart(cc2.CalculateValue); // Create the thread objects and start the threads. In this // case, when we call Start(), we pass in the double array // as an argument. Thread secondaryThread1 = new Thread(threadedMethod1); Thread secondaryThread2 = new Thread(threadedMethod2); // Set the name of the secondary threads. secondaryThread1.Name = "Calculation Thread #1"; secondaryThread2.Name = "Calculation Thread #2"; // Set the priority of the second thread to be quite a // higher than the other two threads. Even though we // start the first thread before the second thread, the // second thread gets a higher priority and calculates // its value first. secondaryThread2.Priority = ThreadPriority.Highest; secondaryThread1.Priority = ThreadPriority.Lowest; // Start the threads. secondaryThread1.Start(numbers1); secondaryThread2.Start(numbers2); // Display some messages to show that Main() is still // responsive while the calculation is going on. Console.WriteLine ("\n{0}: Now I'm going to go do something else.", threadId); System.Threading.Thread.Sleep(1500); Console.WriteLine("\n{0}: Like talk about the weather.", threadId); System.Threading.Thread.Sleep(1500); Console.WriteLine("\n{0}: Or the latest news.", threadId); System.Threading.Thread.Sleep(1500); Console.WriteLine("\n{0}: You know, my foot hurts.", threadId); System.Threading.Thread.Sleep(1500); Console.WriteLine("\n{0}: I love hotdogs!", threadId); System.Threading.Thread.Sleep(1500); Console.WriteLine ("\n{0}: How much is a shake at Burgermaster?", threadId); System.Threading.Thread.Sleep(1500); Console.WriteLine("\n{0}: Ok, now I'm getting hungry!", threadId); System.Threading.Thread.Sleep(1500); // Join one of the secondary threads. if (JoinThread(threadId, secondaryThread1)) { Console.WriteLine("\n{0}: The result from {1} is: {2}", threadId, secondaryThread1.ManagedThreadId, cc1.Results); } // Join the other secondary thread. if (JoinThread(threadId, secondaryThread2)) { Console.WriteLine("\n{0}: The result from {1} is: {2}", threadId, secondaryThread2.ManagedThreadId, cc2.Results); } } catch (Exception e) { Console.WriteLine("\n{0}: EXCEPTION: {1}.", threadId, e.Message); } // Pause so we can look at the console window. Console.Write("\n\n{0}: Press <ENTER> to end: ", threadId); Console.ReadLine(); }