Issue dated - 17th February 2003

-


CURRENT ISSUE
INDIA NEWS
NASSCOM Sp.
STOCK FILE
OPINION
TECHSPACE
PRODUCTS
EVENTS
COLUMNS
TECH FORUM

THE C# COLUMN

BETWEEN THE BYTES
TECHNOLOGY
SPECIALS <NEW>
HMA BANKBIZ
EC SERVICES
ARCHIVES/SEARCH
IT APPOINTMENTS
WRITE TO US
SUBSCRIBE/RENEW
CUSTOMER SERVICE
ADVERTISE
ABOUT US

 Network Sites
  IT People
  Network Magazine
  Business Traveller
  Exp. Hotelier & Caterer
  Exp. Travel & Tourism
  Exp. Backwaters
  Exp. Pharma Pulse
  Exp. Healthcare Mgmt.
  Express Textile
 Group Sites
  ExpressIndia
  Indian Express
  Financial Express

 
Front Page > TechSpace > Story Print this Page|  Email this page

Effective synchronisation of threads

The C# Column - Yashawant Kanetkar

Synchronisation plays an important role in multithreaded applications where the threads are not independent of each other and share common resources. This is because if two threads share common resources and try to manipulate the resources simultaneously, the resources become inconsistent. To tackle such situations synchronisation provides a lock on the resource that is shared among threads and makes a thread wait until the other thread finishes the job with the resource. Thus synchronisation ‘locks’ the shared resource and prevents another thread from using it.

C# provides a lock keyword to lock the shared object or resource. Whatever is written inside the parenthesis following the lock keyword gets locked for the current thread and no other thread is permitted to obtain the lock on that resource or object. We can write an expression that evaluates to an object, or just the object on which we wish to have a lock, inside the parenthesis. Whatever the expression may be, the result should be a reference type. The code that uses the object to be synchronised should be written inside the block of the lock statement.
To give you a more concrete example, consider two threads working on a file. If both the threads try to open the file and write to it simultaneously, an exception is thrown. Here what we need to do is synchronise both the threads in such a way that only one thread opens the file and writes in it at any given moment.

We have illustrated the same scenario in the example that follows. The User Interface of the application is shown in the following screen shot:

Here we have added a textbox named quantity, a button called purchase and a label called msg. In this application we have simply asked the user the number of goods he wants to purchase, i.e. the demand. We have maintained number of goods in stock in a file called log.bin. Whenever the user purchases a certain amount of goods, the number in the file is updated.

In this application we have also launched another thread which keeps on checking continuously whether the goods are out of stock or not. If the thread finds that the number of goods in the store is zero, it fills the store with hundred items and hence updates the number in log.bin file.

We need to synchronise the two threads so that only one thread works on log.bin at one time. We added the following as data members of the Form1 class.

FileInfo f;
FileStream fs;
byte total=100;

total denotes the total number of goods in store. We have used a value like 100 that can be represented in one byte. This is because FileStream is used to write an array of bytes in the file and if we take a larger number, we would have to first create an array of bytes representing the large number. For the sake of simplicity here we have used a number that can be represented in one byte.

We initialised the reference f, and launched the thread that would keep on checking the stock from time to time after the call to the InitializeComponent( ) method in the constructor as shown below:

f = new FileInfo ( “C:\\log.bin” );
Thread t = new Thread (new ThreadStart(checker));
t.IsBackground = true;
t.Start();

We have made this thread a background thread because we wish that as soon as the application terminates, the thread should also get terminated. The checker( ) method is given below:

public void checker()
{
   while ( true )
   {
	   lock (f)
		{
			try
			{
				fs = f.OpenWrite( ) ;
				if ( total <= 0 )
				{
					total = ( byte ) ( total + 100 ) ; 
					fs.WriteByte ( total ) ;
					msg.Text = “Stock sold out!!!! Goods incremented” ;
				}
				else 
				msg.Text = “Items in stock: “ + total ;
				Thread.Sleep ( 1000 ) ;
				fs.Close();
			}
			catch
			{
				MessageBox.Show ( “Can not Open file” ) ;
			}
   	}
   }
}

We have put the checker logic in a while loop so that the store is checked continuously after every 1000 milliseconds (Thread.Sleep(1000) ensures this). Next we have obtained a lock on the file object referred to by f using the lock statement. After acquiring the lock we have checked whether total is zero or less than zero. If yes, we have incremented total by 100 and written the value back in the file. If it is not zero or less than zero, we have simply displayed the number of goods in the label named msg. We have put the whole logic in a try-catch block, the reason for which would be clarified later.

Now if the user supplies some number in the textbox and clicks on the purchase button, the following handler gets called:

private void purchase_Click    ( object sender, System.EventArgs e )
{
	lock ( f )
	{
		try
		{
			fs = f.OpenWrite( ) ;
			byte b = byte.Parse ( quantity.Text ) ;
			total = ( byte ) ( total - b ) ; 
			fs.WriteByte ( total ) ;
			MessageBox.Show ( “Transaction Complete” + “\n” + “Item    
			remaining: “ + total ) ;
			fs.Close( ) ;
		}
		catch
		{
			MessageBox.Show ( “Cannot Open file” ) ;
		}
   }
}

Here also we have first acquired a lock on the file object. Next we have collected the quantity demanded by the user in a byte called b. Then we have subtracted that amount from total and written back the new value of total in the file. We have put this logic also in a try-catch block. This method gets executed in the main thread, whereas checker( ) gets executed in a thread referred to by t.

Now if you execute the program, you would see that the number of goods in the store is displayed continuously on the msg label. As soon as the user purchases some goods, the number is updated and when the number goes below zero, the goods are incremented.

This works fine, but if you really want to understand the need of synchronisation, remove or comment the lock statement and see the effect. As soon as the user clicks on purchase, an exception gets thrown indicating that the file is already in use. To catch this exception we have used the try-catch blocks. The exception gets thrown because both the threads try to open and write to the file simultaneously.

Besides the lock statement, synchronisation can also be achieved in C# using the Interlocked class, the Monitor class, Mutexes and Events.

The Interlocked class provides four methods to perform operations like incrementing, decrementing or exchanging variables in a thread-safe manner. The four static methods are—Increment( ), Decrement( ), Exchange( ), and CompareExchange().

Monitors: In .NET, Monitors are similar to the lock statement and are used to synchronise concurrent thread accesses so that any resource can be manipulated only by one thread at a time. Thus they support mutually exclusive access to a shared resource. Monitors are similar to Critical Sections in Windows. The Framework Class library provides a Monitor class to represent the Monitor. The above program will also work if we use Monitor.Enter( f ) and Monitor.Exit( f ) methods, belonging to the Monitor class, instead of the lock statement. All we need to do is to write the code present in the lock block between the calls to Monitor.Enter( f ) and Monitor.Exit( f ) methods. In fact when we use the lock statement it gets resolved into calls to Monitor.Enter( ) and Monitor.Exit( ) methods in the IL code.

Mutexes: The word mutex comes from two words-”mutually’ and “exclusive”. Mutexes are similar to monitors except that they permit the object or resource to be shared across processes. Mutexes can thus synchronise threads belonging to different applications. The System.Threading.Mutex class represents a mutex object.

Events: are used to synchronise threads in some order. For example if two threads are executing and we want that not only should the threads work synchronously, but they should also get executed alternately, we should use events. If we use events we will get the following order of execution:

	Thread1 executing
	Thread2 executing
	Thread1 executing
	Thread2 executing

And so on... But instead if we use only monitors, we will achieve synchronisation but threads are likely to get executed in the following manner:

	Thread1 executing
   Thread1 executing
   Thread2 executing
   Thread1 executing

The .NET Framework class library wraps two of the Windows’ kernel objects-auto-reset events and manual-reset events-into two classes called AutoResetEvent and ManualResetEvent.

Yashavant Kanetkar, one of the first Express Computer columnists, is an established software expert, speaker and author with several best-sellers to his credit, including titles like “Let Us C” and the “Fundas” series. Contact him at kanet@nagpur.dot.net.in
<Back to top>


© Copyright 2000: Indian Express Group (Mumbai, India). All rights reserved throughout the world. This entire site is compiled in
Mumbai by The Business Publications Division of the Indian Express Group of Newspapers.
Please contact our Webmaster for any queries on this site.