Creating the ThreadSynch Project

   

Now that you know the possible states a thread can be placed in, you will create a project that at first creates two threads. Later, you will add synchronization and a data-access simulation. Create a project by clicking New, Project from the File menu. Select the Visual C++ Project item from the list of project types and select the Managed C++ Application project template. Give your project the name ThreadSynch and click OK to create the project.

This application is going to be a common design paradigm consisting of a producer object and a consumer object. The producer object, which consists of a single thread, will create a single data object. It will then notify the consumer object, which is on a separate thread, that a block of data is ready. Once the consumer uses the object, it then notifies the producer that it is finished. This process then repeats a set number of times.

The current task is to create the two threads and set the states to Running. Therefore, create two managed classes, called Producer and Consumer. For now, just create a default constructor for both of the classes. You can use Listing 19.1 as a guide while you follow along.

As mentioned earlier, each thread has one method that is used as the main thread procedure. When the thread is entered, it is placed in the Running state. When the method returns, the thread is in the final Stopped state and cannot be restarted. The delegate method can be any name you wish and does not take any parameters and does not return any values. In both classes, create a method named ThreadRun that will be used as the delegate for the respective classes. Finally, just so you know the threads are being created and their delegates are being called, output some text to the console using Console::WriteLine.

The next step is the actual creation and execution of the threads within your _tmain function. Creating a thread object is the same as creating any other instance of an object. Therefore, create a pointer to a Consumer class and a pointer to a Producer class and instantiate each one using the C++ keyword new, as shown on line 36 of Listing 19.1. This just creates the actual consumer and producer objects. In order to run them on separate threads, you have to create a thread object for each one, passing the Thread constructor a pointer to the delegate used within each class. The delegate name used for the Thread class is ThreadStart.

Listing 19.1 Creating a Multiple-Threaded Application
 1: #include "stdafx.h"  2:  3: #using <mscorlib.dll>  4: #include <tchar.h>  5:  6: using namespace System;  7: using namespace System::Threading;  8:  9: public __gc class Consumer 10: { 11: public: 12:     Consumer(){} 13: 14:     void ThreadRun( ) 15:     { 16:         Console::WriteLine("Consumer Thread Created" ); 17:     } 18: }; 19: 20: public __gc class Producer 21: { 22: public: 23: 24:     Producer(){} 25: 26:     void ThreadRun() 27:     { 28:       Console::WriteLine( "Producer Thread Created" ); 29:     } 30: }; 31: 32: // This is the entry point for this application 33: int _tmain(void) 34: { 35:     int nResult = 0; 36:     Producer* pProducer = new Producer(); 37: 38:     Consumer* pConsumer = new Consumer(); 39: 40:     Thread* pProdThread = new Thread(new ThreadStart 41:         (pProducer, &Producer::ThreadRun )); 42:     Thread* pconsThread = new Thread(new ThreadStart 43:         (pConsumer, &Consumer::ThreadRun )); 44: 45:     try 46:     { 47:         pProdThread->Start( ); 48:         pconsThread->Start( ); 49: 50:         pProdThread->Join( ); 51:         pconsThread->Join( ); 52:     } 53:     catch (ThreadStateException* e) 54:     { 55:         Console::WriteLine(e->Message); 56:         nResult = 1; 57:     } 58:     catch (ThreadInterruptedException* e) 59:     { 60:         Console::WriteLine(e->Message); 61:         nResult = 1; 62:     } 63: 64:     Environment::ExitCode = nResult; 65: } 

Even though you've created the objects that will be used within the threads and you've created the actual thread objects, they are still in the Unstarted state. In order to place the threads in the Running state, you call the Start method on each of the Thread objects. On line 48 of Listing 19.1, you can see that the method Join is called on each thread. Calling Join will block the current thread, the main thread executing in the _tmain function, until each of the threads finishes. This is similar to calling the WIN32 API function WaitForSingleObject, but rather than you having to worry about thread handles, the .NET Framework wraps thread-controlling functions nicely for you within the Thread class.

Like many other .NET Framework based objects, the Thread class can throw exceptions to let the caller know that something out of the ordinary has happened. This application doesn't catch every possible thread exception, but it does illustrate how to catch an exception if you try to restart a thread after it has either terminated or is already running. This exception is the ThreadStateException. The other exception, ThreadInterruptedException is thrown when another thread calls Interrupt on a thread that is in the WaitSleepJoin state.

In order to verify that your threads are being created and started correctly, compile and run your application. Your output should appear similar to Figure 19.3.

Figure 19.3. Output showing the creation and running of two separate threads.

graphics/19fig03.jpg


   
Top


Sams Teach Yourself Visual C++. NET in 24 Hours
Sams Teach Yourself Visual C++.NET in 24 Hours
ISBN: 0672323230
EAN: 2147483647
Year: 2002
Pages: 237

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