|
Thread Synchronization Using Mutex |
|
|
The word mutex comes from mutually
and exclusive. Mutexe is used to gain exclusive access to a resource
mutually shared by two or more threads. Unlike critical sections, mutexes can be
used to synchronize threads running in the same process or in different
processes. There is one more difference between the two. If a thread locks a
critical section and terminates without unlocking it, other threads waiting for
the critical section to become free will be blocked indefinitely. However, if a
thread that locks a mutex fails to unlock it before terminating, the system
automatically frees the mutex so that waiting threads can resume. To synchronize
threads within a process critical sections are preferred since critical sections
are faster than mutexes.
MFC provides a class CMutex to permit thread synchronisation. Let us now
see how to put this class to use. Suppose there is a file resource which we wish
to share across two threads belonging to two different processes. One thread
opens the file, sorts its contents, writes the sorted contents back to the file
and finally closes it. The other thread opens the same file and displays its
contents in an edit box. If one of the threads is in progress the other should
get blocked. This synchronization is accomplished using the CMutex class.
Let us now see how to achieve this programmatically.
Build an SDI application ‘mutex1’ without Doc/View support. Delete all the
existing menu items and then add an item ‘Sort’ (having ID IDC_SORT) to it. #include
‘Afxmt.h’ file in ‘MainFrm.cpp’ file to make the CMutex class
available. Add a global variable mutex of type CMutex *.
Modify the constructor of CMainFrame class as shown below:
Here, we have started the thread
by calling AfxBeginThread( ) function. Declare the thread function
in ‘MainFrm.cpp’ as given below:
UINT sortthread ( LPVOID param ) ;
Write the thread function sortthread as given below:
UINT sortthread ( LPVOID param ) ;
{
mutex -> Lock( ) ;
CString str[300], temp ;
CStdioFile f ;
if ( f.Open ( "c:\\text.txt", CFile::modeReadWrite
) == 0 )
{
AfxMessageBox ( "Cannot open
file" ) ;
return TRUE ;
}
int count = 0 ;
while ( f.ReadString (
str[count] ) )
count++ ;
for ( int j = 0 ; j < count - 1 ; j++ )
{
for
( int k = j + 1 ; k < count ; k++ )
{
if
( str[j] > str[k] )
{
temp = str[j] ;
str[j] = str[k] ;
str[k] = temp ;
}
}
}
f.SeekToBegin( ) ;
for ( int i = 0 ; i
< count ; i++ )
{
f.WriteString
( str[i] ) ;
f.WriteString
( "\n" ) ;
}
f.Close( ) ;
mutex -> Unlock( ) ;
return 0 ;
}
Here, we have locked the mutex by calling CMutex::Lock( ). Next, we have
opened the file, sorted its contents and written the sorted contents back to the
file. Thereafter, we have unlocked the mutex using CMutex::Unlock( )
function.
By default, Lock( ) will wait forever for a mutex to become unlocked.
Alternatively, we can specify a maximum wait time in milliseconds. In such case
if Lock( ) returns 0, it means that time out period expired first,
and if it returns a non-zero value it means that the mutex came free.
Now create another SDI application ‘mutex2’ without Doc/View support. Delete
all the existing menu items and then add a menu ‘Read’ having ID IDC_READ. #include
‘Afxmt.h’ file in ‘MainFrm.cpp’ file. Add global variables mutex
of type CMutex* and edit of type CEdit.
In the constructor of CMainFrame class write the following statement:
CMainFrame::CMainFrame( )
{
mutex
= new CMutex ( FALSE, "filemutex" ) ;
}
Note that the second parameter passed to the constructor is same as one we have passed while creating CMutex’s object in ‘mutex1’ application.
Modify CMainFrame::OnCreate( )
as shown below:
int CMainFrame::OnCreate ( LPCREATESTRUCT lpCreateStruct )
{
// Code added by AppWizard
edit.Create
( WS_CHILD | WS_VISIBLE | ES_MULTILINE, rectDefault, this,AFX_IDW_PANE_FIRST
) ;.
return
0 ;
}
In this function, we have created an edit box where the contents of the file
will be displayed.
Add a menu handler OnRead( ) for the ‘Read’ menu item. Write the
following code in it.
void CMainFrame::OnRead( )
{
AfxBeginThread
( readthread, NULL ) ;
}
Here, we have started a thread readthread which is given below:
UINT readthread ( LPVOID param )
{
mutex
-> Lock( ) ;
CString
str, editstr ;
CStdioFile
f ;
if (
f.Open ( "c:\\text.txt", CFile::modeReadWrite ) == 0 )
{
AfxMessageBox
( "Cannot open file" ) ;
return
TRUE ;
}
while ( f.ReadString ( str ) )
editstr
+= str ;
edit.SetWindowText
( editstr ) ;
f.Close(
) ;
mutex
-> Unlock( ) ;
return
0 ;
}
Declare the thread function in ‘MainFrm.cpp’ as
UINT readthread ( LPVOID param ) ;
Here, again we have locked the mutex, read the file and displayed the contents
in the edit box by calling CWnd::SetWindowText( ) function.
Create a file ‘text.txt’ in ‘C:\’. The file must be big enough so that
sorting will take more than a couple of seconds. Run both the applications.
Click the ‘Sort’ menu from one application and immediately click the ‘Read’
menu from the other. The thread that reads the file waits until sorting
completes and then displays the contents. On the other hand had we not
synchronized the threads and tried to read the file while sorting is in progress
a message box would have popped up reporting an error ‘Cannot open file’.