To get over this problem, you must be able to synchronize access to objects, so that data can only be accessed by one thread at a time.
One of the most common functions your program will have to perform is to increment or decrement data. For example, you may have to keep an inventory of stock and take note of units that are bought and sold. To make sure your data is stable, you must make sure that it can only be changed by one thread at a time.
The Interlocked class synchronizes access to data, takes a reference to a counter variable as an argument, and uses the Increment and Decrement methods to raise or lower the value of the variable. The code in this example allows one thread at a time to access the variable and increase it by i.
The Interlocked class is used solely for incrementing and decrementing variables, but the lock statement can be used to perform a wider variety of functions.
public void CountMethod()
{
while (i < 100)
{
Interlocked.Increment(ref i);
Thread.Sleep(1);
Console.WriteLine("{0};Count:{1}",Thread.CurrentThread.Name,i);
}
}
The syntax for the lock statement is
lock (expression) {statement}
The lock statement takes an object as an argument. The object is held in stasis and can't be accessed by other threads until the attached statement block is completed.
public void CountMethod()
{
while (i < 100)
{
lock (this)
{
i--;
Thread.Sleep(1);
Console.WriteLine("{0};Count:{1}",Thread.CurrentThread.Name,i);
}
}
}
The Monitor class is the most sophisticated way of synchronizing resources and gives you most control over the order in which your threads execute.
The Enter method is the first element of the Monitor class you will call. This locks the object you are going to use, to prevent other threads from accessing it. If the object is already in use by another resource, the Monitor will wait until it can lock the resource.
The Wait method defines a condition that you want to have occur before the monitor will unlock the resource. This could include a change in the value of a flagged variable, a counter reaching a defined number, or some other signal.
The Pulse method may also be called on the monitor. This method indicates that a change has occurred that may cause a waiting thread to be resumed.
public void CountUpMethod()
{
try
{
Monitor.Enter(this);
if (i > 50)
{
Console.WriteLine("{0}:Count:{1}:(< 10)Must Wait",Thread.CurrentThread.Name, i);
Monitor.Wait(this);
}
}
finally
{
Monitor.Exit(this);
Console.WriteLine("Closing Monitor for {0}",Thread.CurrentThread.Name);
}
}
When a thread is finished with the locked resource, the Monitor is ended and the lock released with the Exit method.
Although synchronization is very useful, and very necessary in many situations, there are two problems in particular that threaded and synchronized programs can suffer from. These are
- Race conditions
- Deadlocking
Race conditions
A race condition occurs when you have two threads that can only successfully run in a particular order but that are started at the same time. If the right thread executes first, there is no problem, but if the wrong thread is first to execute, it could cause unexpected results.
To avoid race conditions, you should make sure that any sequence-dependent situations of this type are restricted to the correct sequence. For example, you could use the Join method to make sure one thread must finish before the other starts, or use the Wait method of the Monitor class to ensure that the conditions are correct before a thread starts.
Deadlocking
You will often use synchronization to lock resources so that they can only be used by one thread. It is also common for threads to be suspended while they wait for a resource to become available. When the two are combined, there is a possibility of a deadlock.
A deadlock occurs when one thread is locking a resource and waiting for another resource to become available, but that resource is itself held by a thread that's waiting for the original resource to become available.
This is relatively easy to spot when there are only two threads involved but can potentially involve many threads, each locking and locked by another, making the situation difficult to debug.
You have now learned about the two particular problems that threaded and synchronized programs can suffer from.
Enjoy and Have fun!!!!!