
Deadlocks and Race Conditions
Multithreading solves problems with throughput and responsiveness, but in doing so it introduces new problems: deadlocks and race conditions.
Deadlocks
A deadlock occurs when each of two threads tries to lock a resource the other has already locked. Neither thread can make any further progress.
Many methods of the managed threading classes provide time-outs to help you detect deadlocks. For example, the following code attempts to acquire a lock on the current instance. If the lock is not obtained in 300 milliseconds, Monitor..::.TryEnter returns false.
If Monitor.TryEnter(Me, 300) Then
Try
' Place code protected by the Monitor here.
Finally
Monitor.Exit(Me)
End Try
Else
' Code to execute if the attempt times out.
End If
if (Monitor.TryEnter(this, 300)) {
try {
// Place code protected by the Monitor here.
}
finally {
Monitor.Exit(this);
}
}
else {
// Code to execute if the attempt times out.
}
Race Conditions
A race condition is a bug that occurs when the outcome of a program depends on which of two or more threads reaches a particular block of code first. Running the program many times produces different results, and the result of any given run cannot be predicted.
A simple example of a race condition is incrementing a field. Suppose a class has a private static field (Shared in Visual Basic) that is incremented every time an instance of the class is created, using code such as objCt++; (C#) or objCt += 1 (Visual Basic). This operation requires loading the value from objCt into a register, incrementing the value, and storing it in objCt.
In a multithreaded application, a thread that has loaded and incremented the value might be preempted by another thread which performs all three steps; when the first thread resumes execution and stores its value, it overwrites objCt without taking into account the fact that the value has changed in the interim.
This particular race condition is easily avoided by using methods of the Interlocked class, such as Interlocked..::.Increment. To read about other techniques for synchronizing data among multiple threads, see Synchronizing Data for Multithreading.
Race conditions can also occur when you synchronize the activities of multiple threads. Whenever you write a line of code, you must consider what might happen if a thread were preempted before executing the line (or before any of the individual machine instructions that make up the line), and another thread overtook it.