We call the work done while the sign is hanging a critical section it is

We call the work done while the sign is hanging a

This preview shows page 36 - 38 out of 64 pages.

there is no sign so that it can be sure it is the only one performing an operation on the bank account. We call the work done “while the sign is hanging” a critical section ; it is critical that such operations not be interleaved with other conflicting ones. (Critical sections often have other technical requirements, but this simple informal definition will suffice for our purposes.) Here is one WRONG way to try to implement this idea. You should never write code that tries to do this manually, but it is worth understanding why it is WRONG: class BankAccount { int balance; bool busy; public: virtual void withdraw(int amount) { while (busy) { /* spin-wait */ } busy = true; int b = getBalance(); if (amount > b) throw insufficientFunds(); setBalance(b - amount); busy = false; } // deposit would spin on same boolean }; The idea is to use the busy variable as our do-not-disturb sign. All the other account operations would use the busy variable in the same way so that only one operation occurs at a time. The while-loop is perhaps a little inefficient since it would be better not to do useless work and instead be notified when the account is “available” for another operation, but that is not the real problem. The busy variable does not work, as this interleaving shows: Thread 1 Thread 2 -------- -------- while(busy) { } while(busy) { } busy = true; busy = true; int b = getBalance(); int b = getBalance(); if(amount > b) throw insufficientFunds(); setBalance(b - amount); if(amount > b) throw insufficientFunds(); setBalance(b - amount); 36
Essentially we have the same problem with busy that we had with balance : we can check that busy is false, but then it might get set to true before we have a chance to set it to true ourselves. We need to check there is no do-not-disturb sign and put our own sign up while we still know there is no sign . Hopefully you can see that using a variable to see if busy is busy is only going to push the problem somewhere else again. To get out of this conundrum, we rely on synchronization primitives provided by the programming language. These typically rely on special hardware instructions that can do a little bit more “all at once” than just read or write a variable. This turns out to be enough to implement mutual exclusion properly. The particular kind of synchronization we will use is locking . 6.3 Locks We can define a mutual-exclusion lock , also known as just a lock (or sometimes called a mutex ), as an abstract datatype designed for concurrent programming and supporting 3 operations: new creates a new lock that is initially “not held” acquire takes a lock and blocks until it is currently “not held” (this could be right away, i.e., not blocking at all, since the lock might already be not held). It sets the lock to “held” and returns. release takes a lock and sets it to “not held.” This is exactly the “do-not-disturb” sign we were looking for, where the acquire operation blocks (does not return) until the caller is the thread that most recently hung the sign. It is up to the lock implementation to ensure that it always does the right thing no matter how many acquires and/or releases different threads perform simultaneously.

  • Left Quote Icon

    Student Picture

  • Left Quote Icon

    Student Picture

  • Left Quote Icon

    Student Picture