next up previous contents
Next: Protocols based on shared Up: Concurrent Programming Previous: Concurrent Programming   Contents

Race conditions

Shared variables provide a simple mechanism for communication between processes. For instance, in the example of the browser discussed earlier, we could have a boolean variable that indicates whether the download should be interrupted. This variable is initially false. When the user-interface thread detects that the "Stop" button has been clicked, it sets the variable to true. Periodically, the download thread examines the value of this variable. If it finds that the variable has been set to true, it aborts the current transfer.

Once we have shared variables, it becomes important to incorporate a mechanism for consistent update of such variables by concurrent threads. Consider a system in which we have two threads that update a shared variable n, as follows:

    Thread 1                          Thread 2

    ...                               ...
    m = n;                            k = n;
    m++;                              k++;
    n = m;                            n = k;
    ...                               ...

Under normal circumstances, after these two segments have executed, we would expect the value of n to have been incremented twice, once by each thread. However, because of time-slicing, the order of execution of the statements may be as follows;

    Thread 1: m = n;
    Thread 1: m++;
    Thread 2: k = n;   // k gets the original value of n
    Thread 2: k++;
    Thread 1: n = m;
    Thread 2: n = k;   // Same value as that set by Thread 1

In this sequence, the increments performed by the two threads overlap and the final value of n is only one more than its initial value. This kind of inconsistent update, whose effect depends on the exact order in which concurrent threads execute, is known as a race condition.

Here is a more complex example of the same phenomenon. Suppose we have a shared array

    double accounts[100]

that holds the current balance for 100 bank accounts. Suppose we have have two functions as follows:

    // transfer "amount" from accounts[source] to accounts[target]
    boolean transfer (double amount, int source, int target){ 
      if (accounts[source] < amount){
        return false;
      }
      accounts[source] -= amount;
      accounts[target] += amount;
      return true;
    }

    // compute the total balance across all accounts
    double audit(){
      double balance = 0.00;
      for (int i = 0; i < 100; i++){
        balance += accounts[i];
      }
      return balance;
    }

Now, suppose we execute these functions in concurrent threads, as follows:

    Thread 1                             Thread 2
    ...                                  ...
    status = transfer(500.00,7,8);       print (audit());
    ...                                  ...

Suppose that Thread 2 gets access to accounts[7] and accounts[8] after Thread 1 has debited accounts[7] but before it has credited accounts[8]. The audit will then report that a sum of 500.00 has been ``lost'' from the accounts in the bank, because it uses the updated value of accounts[7] but the old value of accounts[8].

The situation is even more complicated than this. Even if Thread 1 executes both the debit and the credit without interruption, it is possible that Thread 2 reads accounts[7] before the transfer and accounts[8] after the transfer, thereby recording an excess of 500.00 in the total balance.

For these functions to execute concurrently in parallel, both transfer(...) and audit() should execute from beginning to end without interruption. Stated another way, it should never be the case that the current control point in one thread is within transfer(...) while in the other thread it is within audit().

All of these examples can be formulated as instances of the familiar problem of mutual exclusion to critical regions of program code, which is well-studied, for instance, in the design of operating systems.


next up previous contents
Next: Protocols based on shared Up: Concurrent Programming Previous: Concurrent Programming   Contents
Madhavan Mukund 2004-04-29