1 / 30

Operating Systems Design (CS 423)

This text discusses synchronizing multiple threads in concurrent programs, including the concept of mutual exclusion, different solutions for managing shared resources, and the use of locks (mutexes) for high-level synchronization.

cgroner
Download Presentation

Operating Systems Design (CS 423)

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. Operating Systems Design (CS 423) Elsa L Gunter 2112 SC, UIUC http://www.cs.illinois.edu/class/cs423/ Based on slides by Roy Campbell, Sam King, and Andrew S Tanenbaum

  2. Synchronizing multiple threads • Must control interleaving between threads • Order of some operations irrelevant • Independent • Other operations are dependent and order does matter • All possible interleaving must yield a correct answer • A correct concurrent program will work no matter how fast the processors are that execute the various threads

  3. Synchronizing multiple threads • All interleavings result in correct answer • Try to constrain the thread executions as little as possible • Controlling the execution and order of threads is called “synchronization”

  4. Too much milk • The Gunter household drinks a lot of milk, but has a small fridge • Problem: Carl and Elsa want there to always at least one gallon of milk in the fridge for dinner; fridge holds at most two gallons • If either sees there is less than one gallon, goes to buy milk • Specification: Someone buys milk if running low • Never more than two gallons of milk milk

  5. Solution #0 – no sync Carl Elsa 5:30 Comes home 5:35 Checks milk 5:40 Goes to store 5:45 Comes home 5:50 Buys milk Checks milk 5:55 Goes home Goes to store 6:00 Puts milk in Fridge Buys milk 6:05 Comes home 6:10 Too much milk!

  6. Mutual Exclusion • Ensure that only 1 thread is doing a certain thing at one time • Only one person goes shopping at one time • Critical section • A section of code that needs to run atomically w.r.t. other code • If code A and code B are critical sections w.r.t. each other threads cannot interleave events from A and B • Critical sections must be atomic w.r.t. each other • Share data (or other resourced, e.g., screen, fridge) • What is the critical section in solution #0?

  7. Too much milk (solution #1) • Assume only atomic operations are load and store • Idea: leave note that going to check on milk status • Carl: if (no note) {if (milk low) {leave note; buy milk; remove note;} • Elsa: if (no note) {if (milk low) {leave note; buy milk; remove note;} • What can go wrong? • Is this better than before?

  8. Too much milk (solution #2) • Idea: Change order of leave note and check milk • Carl: if (milk low) {if (no note) {leave note; buy milk; remove note;} • Elsa: if (milk low) {if (no note) {leave note; buy milk; remove note;} • What can go wrong? • Is this better than before?

  9. Too much milk (solution #3) • Idea: Protect more actions with note • Carl: if (no note) {leave note; if (milk low) {buy milk}; remove note;} • Elsa: if (no note) {leave note; if (milk low) {buy milk}; remove note;} • What can go wrong? • Is this better than before?

  10. Too much milk (solution #4) • Idea: Change order of leaving note and checking note • Carl: leave noteCarl; if (no noteElsa) { if (milk low) {buy milk};} ; remove noteCarl • Elsa: leave noteElsa; if (no noteCarl) { if (milk low) {buy milk};} ; remove noteElsa • What can go wrong? • Is this better than before?

  11. Too much milk (solution #5) • Idea: When both leave note, always give priority to fixed one to buy milk • Carl: leave noteCarl; while (noteElsa) {do nothing}; if (milk low) {buy milk}; remove noteCarl • Elsa: leave noteElsa; if (no noteCarl) { if (milk low) {buy milk};} ; remove noteElsa Simplified instance of Bakery Algorithm

  12. Too much milk (solution #5) • while (noteElsa) for Carl prevents him from buying milk at same time as Elsa • Proof of correctness • Two parts: Will never have two people buying milk at same time • Will always have someone able to buy milk if it is needed • Correct, but ugly • Complicated, Asymmetric, Inefficient • Carl wastes time while waiting (Busy Waiting)

  13. Higher-level synchronization • Problem: could solve “too much milk” using atomic loads/stores, but messy • Solution: raise the level of abstraction to make life easier for the programmer Concurrent programs High-level synchronization provided by software Low-level atomic operations provided by hardware

  14. Locks (mutexes) • A lock is used to prevent another thread from entering a critical section • Two operations • Lock(): wait until lock is free, then acquire do {if (lock == LOCK_FREE) { lock = LOCK_SET; break;} } while(1) • Unlock(): lock = LOCK_FREE

  15. Locks (mutexes) • Why was the “note” in Too Much Milk solutions #1 and #2 not a good lock? • Four elements of using locks • Lock is initialized to be free • Acquire lock before entering a critical section • Wait to acquire lock if another thread already holds • Release lock after exiting critical section • All synchronization involves waiting • Thread can be running, or blocked (waiting)

  16. Locks • Locks -- shared variable among all thread • Multiple threads share locks • Only affects threads that try to acquire locks • Important: Lock acquisition is atomic!

  17. Lock Variables • Critical section -- part of the program where threads access shared (global) state • Locks -- shared variables used to enforce mutual exclusion • Can have multiple lock variables

  18. Locks (mutexes) • Locks make “Too Much Milk” really easy to solve! • Correct but inefficient • How to reduce waiting for lock? How to reduce time lock is held? Elsa: lock(frigdelock); if (milk low) {buy milk} unlock(fridgelock) Carl: lock(frigdelock); if (milk low) {buy milk} unlock(fridgelock)

  19. Too Much Milk – Solution 7 • Does the following work? lock(); if (milk low & no note) { leave note; unlock(); buy milk; remove note; } else { unlock() }

  20. Too Much Milk – Solution 7 • Does the following work? lock(); if (milk low & no note) { leave note; unlock(); buy milk; lock(); remove note; unlock(); } else { unlock() }

  21. Queues without Locks enqueue (new_element, head) { // find tail of queue for(ptr=head; ptr->next != NULL; ptr = ptr->next); // add new element to tail ptr->next = new_element; new_element->next = NULL; }

  22. Queues without Locks dequeue(head, element) { element = NULL; // if something on queue, remove it if(head->next != NULL) { element = head->next; head->next = head->next->next;} return element; } • What bad things can happen if two threads manipulate the queue at the same time?

  23. Thread-safe Queues with Locks enqueue (new_elt, head) { lock(queuelock); // find tail of queue for(ptr=head; ptr->next != NULL; ptr = ptr->next); // add new element to tail ptr->next = new_elt; new_elt->next = NULL; unlock(queuelock); } dequeue(head, elt) { lock(queuelock); element = NULL; // remove if possible if(head->next != NULL) { elt = head->next; head->next = head->next->next;} unlock(queuelock); return elt; }

  24. Invariants for multi-threaded queue • Can enqueue() unlock anywhere? • Stable state called an invariant • I.e., something that is “always” true • Is the invariant ever allowed to be false?

  25. Invariants for multi-threaded queue • In general, must hold lock when manipulating shared data • What if you’re only reading shared data?

  26. Enqueue – Can we do better? enqueue() { lock find tail of queue unlock lock add new element to tail of queue unlock } • Is this better?

  27. Dequeue if empty? • What if you wanted to have dequeue() wait if the queue is empty? • Could spin in a loop: dequeue() { lock(queuelock); element = NULL; while (head-next == NULL) {wait;}; if(head->next != NULL) { element = head->next; head->next = head->next->next;} unlock(queuelock); return element; }

  28. Problem lock(queuelock); … while (head-next == NULL) {wait;}; • Holding lock while waiting • No one else can access list (if they observe the lock) • Wait forever

  29. Dequeue if empty – Try 2 • Could release the lock before spinning: lock(queuelock); … unlock(queuelock); while (head-next == NULL) {wait;}; • Will this work?

  30. Dequeue if empty – Try 3 • Could release lock and acquire lock on every iteration lock(queuelock); … while (head-next == NULL) {unlock(queuelock); lock(queuelock);}; • This will work, but very ineffecient.

More Related