Critical
section and mutex allow both the threads to execute at the same time. But only
one thread can access the blocking object at a time. However, sometimes we may
wish to write two threads where one thread should not get executed unless the
second thread signals it to do so or unless some event takes place. For example,
if one thread (thread A) fills a buffer with data and another thread (thread B)
processes that data, then thread B must wait for a signal from thread A saying
that the buffer has been initialized. This is possible using Win32 event objects
that have been encapsulated in the CEvent class. An event is a flag in
the OS kernel. This flag can enjoy two states: set or reset. A set event is said
to be in singaled state, whereas, a reset event is saifd to be in non-signaled
state. Like a mutex, events too can be used within a process or across the
processes.
Windows supports two different types of events: autoreset events and
manual-reset events. The difference between them is that an autoreset event is
automatically reset to the nonsignaled (unavailable) state when a thread
blocking on it is released. A manual-reset event doesn’t reset automatically;
it must be reset programmatically. An autoreset event is used when just one
thread is blocked by the event. Manual reset event is used when two or more
threads are blocked by the event.
Let us see an example in which events are used to synchronize two threads
belonging to the same process. In this example one thread reads information
about a line i.e. co-ordinates, color and thickness from a file and stores the
information in an object array (of type CObArray). The other thread
displays the lines according to the details stored in the CObArray.
Naturally, the thread that displays the lines should not start unless CObArray
is initialized by the other thread.
Create an SDI application ‘Events’ with Doc/View architecture support. Add a
menu ‘Display’ (having id ID_DISPLAY) to the existing menu. Create a global
structure in ‘eventsView.h’ as given below:
struct data : public CObject
{
CPoint stpt, endpt ;
int thick ;
COLORREF rgb ;
} ;
Note that we have derived the structure from CObject class since we will
be storing address of objects of this structure in a CObArray. Add
variable m_pdc of type CDC* and a static variable m_ob
of type CObArray to the CEventsView class. Add the
following statement in ‘eventsView.cpp’:
CObArray CEventsView::m_ob ;
Create a global object of CEvent class as shown below:
CEvent event ( FALSE, FALSE ) ;
The first parameter, specifies whether the event object is initially signaled
(TRUE) or nonsignaled (FALSE). Second parameter specifies whether the event is a
manual-reset event (TRUE) or an autoreset event (FALSE). The third parameter
assigns a name to the event object. This parameter is used if the threads are in
different processes. The final parameter is a pointer to a SECURITY_ATTRIBUTES
structure describing the object’s security attributes. We have specified only
first two parameters, for others default values are assumed.
Set the initial size of CObArray object by calling CObArray::SetSize( )
from the constructor as given below:
CEventsView::CEventsView( )
{
m_ob.SetSize ( 0, 10 ) ;
}
The first parameter passed to the SetSize( ) function specifies the
initial size of array. The second parameter is the number of elements to be
allocated to the array if increase in size is necessary.
Add a handler OnDisplay( ) for the ‘Display’ menu. The code of OnDisplay(
) is given below:
void CEventsView::OnDisplay( )
{
AfxBeginThread ( datathread,
&m_ob ) ;
m_pdc = new CClientDC ( this ) ;
AfxBeginThread ( dispthread,
m_pdc ) ;
}
Here, we have started two threads, datathreads and dispthread. The
datathread reads the information of lines from file and stores it in an
array, whereas, dispthread displays the lines in the window. Before
starting dispthread we have initialised the data member m_pdc and
passed it to the thread.
The thread function datathread( ) is given below:
UINT datathread ( LPVOID param )
{
CStdioFile f ;
if ( f.Open ( "objects.dat",
CFile::modeRead ) == 0 )
{
AfxMessageBox
( "File Not Found" ) ;
return 1 ;
}
CString str ;
int left, top, right, bottom, r,
g, b, th ;
data *m_dt ;
while ( f.ReadString ( str ) !=
NULL )
{
sscanf ( str,
"%d %d %d %d %d %d %d %d", &left, &top,&right,
&bottom, &r, &g, &b, &th ) ;
m_dt = new
data ;
m_dt ->
stpt = CPoint ( left, top ) ;
m_dt ->
endpt = CPoint ( right, bottom ) ;
m_dt ->
thick = th ;
m_dt -> rgb
= RGB ( r, g, b ) ;
CEventsView::m_ob.Add
( m_dt ) ;
}
f.Close( ) ;
event.SetEvent( ) ;
return 0 ;
}
Here, we have opened the data file (This file should be created as an ordinary
text file.) The information present in the data file is given below:
90 100 250 210 0 255 0 2100 100 100 210 0 255 0 2
Here, first four digits are the co-ordinates of line, next are the RGB elements
and last digit is the thickness of pen. Next read the data and store them in the
elements of data structure. The object of the structure is then added to
the array by calling CObArray::Add( ) function. Once all the records have
been stored in the array we have released the dispthread by calling CEvent::SetEvent(
) function. If there are multiple threads locked on an event they are
released by calling CEvent::PulseEvent( ) function. PulseEvent(
) function sets the state of the event to signaled state, releases
any waiting threads, and resets it to non-signaled state automatically.
The
dispthread( ) function is given below:
UINT dispthread ( LPVOID param )
{
CDC *pdc = ( CDC * ) param ;
event.Lock( ) ;
data *pd ;
CPen mypen ;
int count =
CEventsView::m_ob.GetSize( ) ;
for ( int i = 0 ; i < count ;
i++ )
{
pd = ( data* )
CEventsView::m_ob[i] ;
if (
mypen.m_hObject != NULL )
mypen.DeleteObject(
) ;
mypen.CreatePen
( PS_SOLID, pd -> thick, pd -> rgb ) ;
pdc ->
SelectObject ( &mypen ) ;
pdc ->
MoveTo ( pd -> stpt ) ;
pdc ->
LineTo ( pd -> endpt ) ;
}
return 0 ;
}
Here, we have first locked the thread by calling CEvent::Lock( ) function
until datathread sets the event. Then we have retrieved the number of
elements stored in the array using CObarray::GetSize( ) function. In a
loop we have obtained the address of the objects of data structure stored
in the array and have drawn the lines using member functions of CDC class.
Suppose we don’t synchronize the threads. Since both the threads have been
started simultaneously the thread dispthread would get executed even
before the array has been initialized. In such a case CObArray::GetSize( )
function would return 0. In such a case the loop would not execute and so
the program would not display any output.
|