Events and Timers

   

You used the Monitor class and its various methods to control data synchronization. Another way you could have achieved this is with the Event kernel object. Events were covered in Hour 18, "Delegates and Events," but the events discussed here are operating system events. When an event is created, it is placed in a non-signaled state. You can then open the event within a different area of code, such as another thread. Once you obtain a handle to the event, you can then wait for it to be signaled. Once the event is signaled, the object waiting can then proceed.

Rather than restructuring the lesson you just created, you are just going to use events and a timer to simulate a long write operation. The code you are going to add will be placed within the IntDataBlock object and will be called when a write operation occurs.

To begin, add a private member variable. This is a pointer to the ManualResetEvent class, which is in the .NET Framework. The two main types of kernel object events are ManualResetEvent and AutoResetEvent. The difference lies in how each of these events is reset. Once an object is signaled, it remains in the signaled state. Therefore, any other object that tries to wait for that event will immediately return because the event is signaled and there is no need to wait. Therefore, an event can be placed back into a non-signaled state by resetting it. ManualResetEvent is reset manually by the developer; AutoResetEvent is automatically reset after the event is signaled. For this application, it doesn't matter which type of event you use because the use of the event is pretty minimal.

Create a private function within the IntDataBlock class called SimulateLongOperation and give it no parameters and a return type of void. Also, create another function named TimerProc. This function accepts a pointer to an Object as its parameter and also returns a void type. You can follow Listing 19.4 to help you make the changes to the IntDataBlock class.

Listing 19.4 An Enhanced Data Object Class Using an Event and Timer to Simulate a Long Operation
 1: public __gc class IntDataBlock  2: {  3: public:  4:     IntDataBlock()  5:     {  6:         m_nLastData = 0;  7:         m_bIsReading = false;  8:     }  9: 10:     void WriteData( int nData ) 11:     { 12:         Monitor::Enter( this ); 13:         { 14:             Console::WriteLine( "Entered Write Monitor" ); 15:             if( m_bIsReading ) 16:             { 17:                 Console::WriteLine( "Write Waiting" ); 18:                 Monitor::Wait( this ); 19:             } 20:             m_nLastData = nData; 21: 22:             Console::Write("Writing: {0}...", __box(nData)); 23:             SimulateLongOperation(); 24:             Console::Write("Done\n"); 25: 26:             m_bIsReading = true; 27:             Console::WriteLine( "Write Pulsing" ); 28:             Monitor::Pulse(this); 29:         } 30:         Console::WriteLine( "Leaving Write Monitor" ); 31:         Monitor::Exit( this ); 32:     } 33: 34:     int ReadData() 35:     { 36:         Monitor::Enter( this ); 37:         { 38:             Console::WriteLine( "Entered Read Monitor" ); 39:             if( !m_bIsReading ) 40:             { 41:                 Console::WriteLine( "Read Waiting" ); 42:                 Monitor::Wait( this ); 43:             } 44:             Console::WriteLine("Reading: {0}\n", __box(m_nLastData)); 45: 46:             m_bIsReading = false; 47:             Console::WriteLine( "Read Pulsing" ); 48:             Monitor::Pulse( this ); 49:         } 50:         Console::WriteLine( "Leaving Read Monitor" ); 51:         Monitor::Exit( this ); 52: 53:         return m_nLastData; 54:     } 55: 56: private: 57:     void TimerProc(Object* state) 58:     { 59:         m_pLongOpEvent->Set(); 60:     } 61: 62:     void SimulateLongOperation() 63:     { 64:         m_pLongOpEvent = new ManualResetEvent(false); 65: 66:         TimerCallback* pTimerDelegate = new TimerCallback(this, TimerProc); 67:         Timer* timer = new Timer 68:             (pTimerDelegate, NULL, 1000, Timeout::Infinite ); 69: 70:         m_pLongOpEvent->WaitOne(); 71:     } 72: 73:     int m_nLastData; 74:     bool m_bIsReading; 75:     ManualResetEvent* m_pLongOpEvent; 76: }; 

The long operation you will simulate will involve creating an event, setting a timer, and then waiting for the event to signal within the timer procedure. Within the SimulateLong Operation function, create a new ManualResetEvent instance and assign it to your private member variable you created earlier. Next, create a timer delegate function using your ThreadProc function as the function pointer to the delegate. This can be seen on line 77 of Listing 19.4. The next step involves creating the timer.

The Timer class accepts four parameters in its constructor. The first parameter is the delegate you created in the previous line. The second parameter can be any object that will be passed to the delegate as long as it is a managed class. The third and fourth parameters control how the delegate is called while the timer is running. The third parameter is how long you want to wait before the timer first calls the delegate. This is known as the due time and is expressed in milliseconds. In this application, you should wait long enough for you to detect that a delay has occurred; 1,000 milliseconds should be good enough. The fourth parameter signifies how many milliseconds to wait before subsequent calls are made to the delegate. Once the due time has passed, the delegate will be called every time that certain number of milliseconds has passed. For this application, you only want the delegate to be called once and no more. You can do this by simple passing Timeout::Infinite as the fourth parameter.

Now that the timer has been created, SimulateLongOperation can wait for the event you created earlier to be signaled. This is done by calling the WaitOne method on the event object. As you may have guessed, the timer delegate will be responsible for signaling the event. After the 1,000 milliseconds has expired, the delegate signals the event by calling the Set member function.

The last step before you can try your application out is to call the SimulateLong Operation function. Place this function call in the WriteData function where the actual simulated write operation takes place.

You are now done with your multiple-threaded and synchronized application. Once you compile and run your application, you will still get the same output as before. This time, however, you will notice a short delay each time the application is simulating a data write.


   
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