Chapter 3 : Thread Synchronization
Lets start with a simple application.
Have a look at the code below:
class BankAccount
{
public int Money;
}
class Program
{
static void
Main (string[] args)
{
BankAccount account = new BankAccount();
account.Money = 100;
Thread oneDollarThread = new Thread(new ParameterizedThreadStart(AddOneDollar));
Thread twoDollarThread = new Thread(new ParameterizedThreadStart(AddTwoDollars));
oneDollarThread.Start(account);
twoDollarThread.Start(account);
Thread.Sleep(4000);
Console.WriteLine("Amount of money in account = " + account.Money);
}
static void AddOneDollar(object param)
{
BankAccount account = (BankAccount)param;
for (int i = 1; i <= 100; i++)
{
int oldamount = account.Money;
oldamount = oldamount + 1;
Thread.Sleep(1);
account.Money = oldamount;
}
}
static void AddTwoDollars(object param)
{
BankAccount account = (BankAccount)param;
for (int i = 1; i <= 100; i++)
{
int oldamount = account.Money;
oldamount = oldamount + 2;
Thread.Sleep(1);
account.Money = oldamount;
}
}
}
In the main function, we create an object of the BankAccount class, and set the initial value of the money field to 100. We then create two threads. The first thread executes the function AddOneDollar, while the second thread executes the function AddTwoDollars. Each of these 2 functions takes as a parameter the BankAccount object we created, and within a loop that iterates 100 times, increases the amount each time by either 1 or 2 dollars.
In the main function, we execute Thread.Sleep(4000) in order to make the main thread sleep (pause) for 4 seconds – this is to give the other two threads ample time to execute. In each of the other two threads, we execute Thread.Sleep(1), this is in order to simulate some sort of processing activity going on within the Bank’s system.
At the end of the 4 seconds, when the main thread wakes up, how much money do you expect to be in the BankAccount? 400?
Have a look at the output I got (your output might be different):
300? What went wrong? Each loop executed 100 times, so, theoretically, the total amount should have been 400 (initially 100, plus 1 * 100, plus 2 * 100).
The reason for the incorrect result is not that hard to determine.
Let us assume that your computer has only one processor (this is the most common scenario). As a result, your computer can only perform one operation at a time. Even though you might be having multiple applications running, with each application having multiple threads running, your computer still performs ONLY ONE operation at a time. Then how do all your applications work simultaneously? Simple – your computer switches between all the running applications very fast. It might allocate a few milliseconds of the processor time to application A to execute. After these few milliseconds, the Operating System pauses application A, and then informs application B to run. After another few milliseconds, the Operating System pauses application B, and then tells application C to run. This whole process happens really fast, and to you – the user – you get the feeling that your computer is actually running all your applications at the exact same time.
Again, within an application, only one thread can run at a time. In our code above, let us assume that at present, the thread corresponding to the function AddOneDollar is runnin, and that the thread corresponding to AddTwoDollars has yet not run. Let us assume that we are in our first iteration (i.e., i=1), and the following two lines have completed executing:
int oldamount = account.Money;
oldamount = oldamount + 1;
Thread.Sleep(1);
At this point, the amount in our BankAccount is yet 100, since we haven’t had a chance to update it. The local variable oldamount however has a value of 101.
After the 3rd line above was executed, let us assume that the Operating System paused this thread, and resumed our other thread, corresponding to AddTwoDollars (since the Operating System periodically pauses the currently running thread in order to give other threads a chance to execute). As a result, assume that the following lines got executed:
int oldamount = account.Money;
oldamount = oldamount + 2;
Thread.Sleep(1);
account.Money = oldamount;
Guess what happens? The first line reads the value of account.Money as 100 (since the value was yet not updated by the first thread) into the local variable oldamount. It increases the value to 102. It then updates the Money field in our global account variable to 102. Assume that after this happens, the Operating System pauses this thread, and goes and wakes up our first thread. The first thread, unaware of what has been going on, goes and updates the Money field to 101! And so, we have a problem.
Attachments
Project Files Thread Synchronization Sample Projects