1 / 116

Ensuring Data Consistency Through Process Synchronization

Learn about critical sections, mutual exclusion, and synchronization methods to maintain orderly process execution. Explore classical synchronization problems and Java & Windows synchronization. Understand race conditions and solutions.

robeson
Download Presentation

Ensuring Data Consistency Through Process Synchronization

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Chapter 7: Process Synchronization

  2. Contents • Background • The Critical-Section Problem • Synchronization Hardware • Semaphores • Classical Problems of Synchronization • Monitors • Java Synchronization • Synchronization in Solaris 2 • Synchronization in Windows NT

  3. § 7.1 Background • Concurrent access to shared data may result in data inconsistency. • Maintaining data consistency requires mechanisms to ensure the orderly execution of cooperating processes. • Shared-memory solution to bounded-butter problem (Chapter 4) has a race condition on the class data count

  4. Bounded Buffer public class BoundedBuffer { public void enter(Object item) { // producer calls this method } public Object remove() { // consumer calls this method } // potential race condition on count private volatile int count; }

  5. enter() Method // producer calls this method public void enter(Object item) { while (count == BUFFER_SIZE) ; // do nothing // add an item to the buffer ++count; buffer[in] = item; in = (in + 1) % BUFFER_SIZE; }

  6. remove() Method // consumer calls this method public Object remove() { Object item; while (count == 0) ; // do nothing // remove an item from the buffer --count; item = buffer[out]; out = (out + 1) % BUFFER_SIZE; return item; }

  7. In machine language • ++countregister1 = count;register1= register1 + 1;count = register1 • --countregister2 = count;register2= register2 – 1;count = register2 • The concurrent execution of the statements ++count and –count is equivalent to a sequential execution where the lower-level statements are interleaved in some arbitrary order.

  8. Race Condition T0: producer execute rigister1 := count {register1=5} T1: producer execute rigister1 := rigister1 +1 {register1=6} T2: consumer execute rigister2 := count {register2=5} T3: consumer execute rigister2 := rigister2 –1 {register2=4} T4: producer execute count := rigister1{count=6} T5: consumer execute count := rigister2{count=4} Should be 5. Error! To guard against the race condition, we need to ensure that only one thread at a time can be manipulating the variable count. To make such a guarantee, we require some form of synchronization of the threads.

  9. Critical-Section Problem § 7.2 • Each thread has a segment of code, called a critical section, in which the thread may be changing common variables, updating a table, writing a file, and so on. • When one thread is executing in its critical section, no other thread is to be allowed to execute in its critical section…mutually exclusive. • The critical-section problem is how to choose a protocol that the threads can use to cooperate.

  10. Critical-Section Problem § 7.2 ( ) When one thread is executing in its critical section, no other thread is to be allowed to execute in its critical section, this is called _________. (A) global data (B) common method (C) mutually exclusive (D) constant accessing (E) value assertion • Each thread has a segment of code, called a critical section, in which the thread may be changing common variables, updating a table, writing a file, and so on. • When one thread is executing in its critical section, no other thread is to be allowed to execute in its critical section…mutually exclusive. • The critical-section problem is how to choose a protocol that the threads can use to cooperate. Answer: C

  11. Critical-Section Problem 1. Mutual Exclusion. If thread Ti is executing in its critical section, then no other threads can be executing in their critical sections. 2. Progress. If no thread is executing in its critical section and there exist some threads that wish to enter their critical sections, then only those threads that are not executing in their noncritical section can participate in the decision of which will enter its critical section next, and this selection cannot be postponed indefinitely. 3. Bounded Waiting. A bound must exist on the number of times that other threads are allowed to enter their critical sections after a process has made a request to enter its critical section and before that request is granted. This bound prevents starvation of any single thread.

  12. Critical-Section Problem Short Answer Question: A solution to the critical-section problem must satisfy three requirements: mutual exclusive, progress, and bounded waiting. Please explain the meaning of these requirements. 1. Mutual Exclusion. If thread Ti is executing in its critical section, then no other threads can be executing in their critical sections. 2. Progress. If no thread is executing in its critical section and there exist some threads that wish to enter their critical sections, then only those threads that are not executing in their noncritical section can participate in the decision of which will enter its critical section next, and this selection cannot be postponed indefinitely. 3. Bounded Waiting. A bound must exist on the number of times that other threads are allowed to enter their critical sections after a process has made a request to enter its critical section and before that request is granted. This bound prevents starvation of any single thread.

  13. Critical-Section Problem Multiple Choice Question: ( ) Which of the following is not one of the requirements for a solution of critical-section problem? (A) progress (B) loopless (C) mutual exclusive (D) bounded waiting 1. Mutual Exclusion. If thread Ti is executing in its critical section, then no other threads can be executing in their critical sections. 2. Progress. If no thread is executing in its critical section and there exist some threads that wish to enter their critical sections, then only those threads that are not executing in their noncritical section can participate in the decision of which will enter its critical section next, and this selection cannot be postponed indefinitely. 3. Bounded Waiting. A bound must exist on the number of times that other threads are allowed to enter their critical sections after a process has made a request to enter its critical section and before that request is granted. This bound prevents starvation of any single thread. Answer: B

  14. Critical-Section Problem • We can make no assumption concerning the relative speed of the n threads. • The solutions should not rely on any assumptions concerning the hardware instructions. • However, the basic machine-language instructions (the primitive instructions such as load, store, and test) are executed atomically.

  15. Critical-Section Problem True-False Question: ( ) When solving critical section problems, we should make proper assumption concerning the relative speed of the threads. • We can make no assumption concerning the relative speed of the n threads. • The solutions should not rely on any assumptions concerning the hardware instructions. • However, the basic machine-language instructions (the primitive instructions such as load, store, and test) are executed atomically. Answer: ×

  16. Two-Tasks Solutions § 7.3 • Each thread is implemented using the worker class. (Fig 7.1) • A call to the methods criticalSection() and nonCriticalSection() represents the places where each thread performs its critical and noncritical sections. • Prior to calling its critical section, each thread will call the method enteringCriticalSection(). • A thread will not return from enteringCriticalSection() until it is able to enter its critical section.

  17. Worker Thread public class Worker extends Thread { public Worker(String n, int i, MutualExclusion s) { name = n; id = i; shared = s; } public void run() { /* see next slide */ } private String name; private int id; private MutualExclusion shared; }

  18. run() Method of Worker Thread Codes in critical section public void run() { while (true) { shared.enteringCriticalSection(id); system.out.println(name + “is in critical section”); MutualExclusion.criticalSection(); shared.leavingCriticalSection(id); System.out.println(name + “is out of critical section”); MutualExclusion.noncriticalSection(); } } Codes out of critical section

  19. MutualExclusion Abstract Class This abstract class will serve as a template for the following three algorithms public abstract class MutualExclusion { public static void criticalSection() { // simulate the critical section } public static void nonCriticalSection() { // simulate the non-critical section } public abstract void enteringCriticalSection(int t); public abstract void leavingCriticalSection(int t); public static final int TURN_0 = 0; public static final int TURN_1 = 1; }

  20. Testing Each Algorithm public class TestAlgorithm { public static void main(String args[]) { MutualExclusion alg = new Algorithm_1(); Worker first = new Worker("Runner 0", 0, alg); Worker second = new Worker("Runner 1", 1, alg); first.start(); second.start(); } } Creating the two threads and used to test each algorithm

  21. Algorithm 1 § 7.3.1 Keeps the thread in the Runnable state, but also allows the JVM to select another Runnable thread of equal priority to run. public class Algorithm_1 extends MutualExclusion { public Algorithm_1() { turn = TURN_0; } public void enteringCriticalSection(int t) { while (turn != t) Thread.yield(); } public void leavingCriticalSection(int t) { turn = 1 - t; } private volatile int turn; }

  22. Algorithm 1 • Ensures that only one thread at a time can be in its critical section. • However, does not satisfy the progress requirement, since it requires strict alternation of threads in the execution of their critical section.

  23. Algorithm 2 public class Algorithm_2 extends MutualExclusion { public Algorithm_2() { flag[0] = false; flag[1] = false; } public void enteringCriticalSection(int t) { int other = 1 - t; flag[t] = true; while (flag[other] == true) Thread.yield(); } public void leavingCriticalSection(int t) { flag[t] = false; } private volatile boolean[] flag = new boolean[2]; }

  24. Algorithm 2 Short Answer Question: Examine the algorithm on the right for solving two-tasks critical-section problem. What this algorithm has achieved and what kind of problem this algorithm is encountered, if any ? public class Algorithm_2 extends MutualExclusion { public Algorithm_2() { flag[0] = false; flag[1] = false; } public void enteringCriticalSection(int t) { int other = 1 - t; flag[t] = true; while (flag[other] == true) Thread.yield(); } public void leavingCriticalSection(int t) { flag[t] = false; } private volatile boolean[] flag = new boolean[2]; }

  25. Algorithm 2 • The mutual-exclusion requirement is satisfied. • Unfortunately, the progress requirement is still not met. • When both threads perform the while statement, they will loop forever as the value of flag for the other thread is true.

  26. Algorithm 3 public class Algorithm_3 extends MutualExclusion { public Algorithm_3() { flag[0] = false; flag[1] = false; turn = TURN_0; } public void enteringCriticalSection(int t) {/* see next slides */ } public void leavingCriticalSection(int t) {{/* see next slides */ } private volatile int turn; private volatile boolean[] flag = new boolean[2]; }

  27. Algorithm 3 public void enteringCriticalSection(int t) { int other = 1 - t; flag[t] = true; turn = other; while ( (flag[other] == true) && (turn == other) ) Thread.yield(); } public void leavingCriticalSection(int t) { flag[t] = false; }

  28. Algorithm 3 • Correct solution to the critical-section problem. • If both threads try to enter at the same time, turn is set to both I and j at roughly the same time. • Only one of these assignments lasts; the other will occur, but will be overwritten immediately. • The eventual value of turn decides which of the two threads is allowed to enter its critical section first.

  29. Synchronization Hardware § 7.4 • Hardware instructions can be used effectively in solving critical-section problem. • It allows as either to test and modify the content of a word, or to swap the contents of two words, atomically--- as one uninterruptible unit.

  30. Synchronization Hardware public class HardwareData { public HardwareData(boolean v) { data = v; } public boolean get() { return data; } public void set(boolean v) { data = v; } private boolean data; }

  31. Test-and-Set Instruction (in Java) public class HardwareSolution { public static boolean testAndSet(HardwareData target) { HardwareData temp = new HardwareData(target.get()); target.set(true); return temp.get(); } }

  32. Test-and-Set Instruction (in Java) True-False Question: ( ) The value of the lock returned by the test-and –set instruction is the value after the instruction is applied onto the lock. public class HardwareSolution { public static boolean testAndSet(HardwareData target) { HardwareData temp = new HardwareData(target.get()); target.set(true); return temp.get(); } } Answer: ×

  33. Thread using Test-and-Set HardwareData lock = new HardwareData(false); while (true) { while (HardwareSolution.testAndSet(lock)) Thread.yield( ); // do nothing criticalSection( ); lock.set(false); nonCriticalSection( ); }

  34. Thread using Test-and-Set Multiple Choices Question: ( ) When calling a test-and-set instruction, a ______ must be provided as its parameter.(a) semaphore (b) integer (c) constant (d) lock HardwareData lock = new HardwareData(false); while (true) { while (HardwareSolution.testAndSet(lock)) Thread.yield( ); // do nothing criticalSection( ); lock.set(false); nonCriticalSection( ); } Answer: d

  35. Thread using Test-and-Set True-False Question: ( ) When the value returned by the test-and-set instruction is false, it means that the locking is not successful. HardwareData lock = new HardwareData(false); while (true) { while (HardwareSolution.testAndSet(lock)) Thread.yield( ); // do nothing criticalSection( ); lock.set(false); nonCriticalSection( ); } Answer: x

  36. Swap instruction • Also executed atomically. public static void swap(HardwareData a, HardwareData b) { HardwareData temp = new HardwareData(a.get()); a.set(b.get()); b.set(temp.get()); }

  37. Thread using Swap HardwareData lock = new HardwareData(false); HardwareData key = new HardwareData(true); while (true) { key.set(true); do { HardwareSolution.swap(lock,key); } while (key.get() == true); criticalSection( ); lock.set(false); nonCriticalSection( ); }

  38. Thread using Swap Multiple Choices Question: ( ) The value of the key used to switch with the lock must be set to ______ before calling the swap method.(a) constant (b) false (c) true (d) integer HardwareData lock = new HardwareData(false); HardwareData key = new HardwareData(true); while (true) { key.set(true); do { HardwareSolution.swap(lock,key); } while (key.get() == true); criticalSection( ); lock.set(false); nonCriticalSection( ); } Answer: c

  39. Semaphore § 7.5 • Previous solutions (§7.3) are not easy to generalize to more complex problems. • Semaphores can be implemented as synchronization tool that does not require busy waiting. • A semaphoreS is an integer variable that can only be accessed via two indivisible (atomic) operations: P and V. P(S) { while S 0 ; // no-op;S--; } These are classical definition of P and V, they require busy waiting. V(S) { S++; }

  40. Semaphore as General Synchronization Tool § 7.5.1 Semaphore S; // initialized to 1 P(S); CriticalSection() V(S); • The value of a counting semaphore can range over an unrestricted domain. The value of a binary semaphore can range only between 0 and 1.

  41. Semaphore Eliminating Busy-Waiting • The main disadvantage of previous solutions: they all require busy waiting…a problem in single CPU, multiprogramming system. • Busy waiting wastes CPU cycles that some other process might be able to use productively. This type of semaphore is also called a spinlock. • Spinlocks are useful in multiprocessor systems. The • advantage of a spinlock is that no context switch is required • when a process must wait on a lock (context switch • may take considerable time). • Spinlocks are useful when locks are expected to be held for • short times.

  42. Semaphore Eliminating Busy-Waiting • The main disadvantage of previous solutions: they all require busy waiting…a problem in single CPU, multiprogramming system. • Busy waiting wastes CPU cycles that some other process might be able to use productively. This type of semaphore is also called a spinlock. • To overcome the need for busy waiting, the definition of P and V are modified.

  43. Semaphore Eliminating Busy-Waiting Short Answer Question: Although spinlocks waist CPU time with busy waiting, they are still useful in some systems. What are the advantage of spinlocks and in which situation they are considered useful? • The main disadvantage of previous solutions: they all require busy waiting…a problem in single CPU, multiprogramming system. • Busy waiting wastes CPU cycles that some other process might be able to use productively. This type of semaphore is also called a spinlock. • To overcome the need for busy waiting, the definition of P and V are modified.

  44. Semaphore Eliminating Busy-Waiting P(S) { value--; if (value < 0) { add this process to list block; } } V(S) { value++; if (value <= 0) { remove a process P from list wakeup(P); } } The block operation places a process into a waiting queue associated with the semaphore, and the state of the process is switched to the waiting state. A process that is blocked, waiting on S, should be restarted when some other process execute a V operation. When the process is restarted by the wakeup operation, the process is changed from the waiting state to the ready state.

  45. Synchronization Using Semaphores public class FirstSemaphore { public static void main(String args[]) { Semaphore sem = new Semaphore(1); Worker[] bees = new Worker[5]; for (int i = 0; i < 5; i++) bees[i] = new Worker(sem, "Worker " + (new Integer(i)).toString() ); for (int i = 0; i < 5; i++) bees[i].start(); } }

  46. Worker Thread public class Worker extends Thread { public Worker(Semaphore s, String n) { name = n; sem = s;} public void run() { while (true) { sem.P( ); // in critical section sem.V( ); // out of critical section } } private Semaphore sem; private String name; }

  47. Deadlock and Starvation the execution of a V operation • Deadlock – two or more processes are waiting indefinitely for an event that can be caused by only one of the waiting processes. • Let S and Q be two semaphores initialized to 1 P0P1 P(S); P(Q); P(Q); P(S);   V(S); V(Q); V(Q) V(S); • Starvation – indefinite blocking. A process may never be removed from the semaphore queue in which it is suspended.

  48. Deadlock and Starvation the execution of a V operation True-False Question: ( ) A deadlock can only happen in between two threads. • Deadlock – two or more processes are waiting indefinitely for an event that can be caused by only one of the waiting processes. • Let S and Q be two semaphores initialized to 1 P0P1 P(S); P(Q); P(Q); P(S);   V(S); V(Q); V(Q) V(S); • Starvation – indefinite blocking. A process may never be removed from the semaphore queue in which it is suspended. Answer: ×

  49. Classical Problems of Synchronization § 7.6 • Bounded-Buffer Problem • Readers and Writers Problem • Dining-Philosophers Problem

  50. Bounded-Buffer Problem § 7.6.1 public class BoundedBuffer { public BoundedBuffer() { /* see next slides */ } public void enter() { /* see next slides */ } public Object remove() { /* see next slides */ } private static final int BUFFER_SIZE = 2; private Semaphore mutex; private Semaphore empty; private Semaphore full; private int in, out; private Object[] buffer; }

More Related