1 Synchronization Threads communicate to ensure consistency If not: race condition...
Transcript of 1 Synchronization Threads communicate to ensure consistency If not: race condition...
1
Synchronization
Threads communicate to ensure consistency If not: race condition
(non-deterministic result) Accomplished by synchronization
operations
How to write concurrent code How to implement synchronization
operations
2
Synchronization – Motivation
“The too much milk problem”
Model of need to synchronize activities
3
Synchronization Terminology
Mutual exclusion (“mutex”) – prevents multiple threads from entering
Critical section – regions of code that modify or access shared variables
– code only one thread can execute at a time
Lock– mechanism for mutual exclusion Lock on entering critical section, accessing
shared data Unlock when complete Wait if locked
4
Solving the Too Much Milk Problem
Correctness properties Safety: “nothing bad happens” Progress: “something good
eventually happens”
First: use atomic loads & stores as building blocks “Leave a note” (lock) “Remove a note” (unlock) “Don’t buy milk if there’s a note” (wait)
5
Too Much Milk: Solution 1
thread A
if (no milk && no note)
leave note
buy milk
remove note
thread B
if (no milk && no note)
leave note
buy milk
remove note
Does this work?too much milk
6
Too Much Milk: Solution 2
thread A
leave note A
if (no note B)
if (no milk)
buy milk
remove note A
thread B
leave note B
if (no note A)
if (no milk)
buy milk
remove note B
Idea: use labeled notes
oops – no milk
7
Language Support
Synchronization complicated Better way – provide language-level
support Higher-level approach Hide gory details in runtime system
Increasingly high-level approaches: Locks, Atomic Operations Semaphores – generalized locks Monitors – tie shared data to
synchronization
8
Locks
Provide mutual exclusion to shared data via two atomic routines Lock::Acquire – wait for lock, then take
it Lock::Release – unlock, wake up waiters
Rules: Acquire lock before accessing shared
data Release lock afterwards Lock – initially released
9
Too Much Milk: Locks
Clean, symmetric - but how do we implement it?
thread A
Lock.acquire()
if (no milk)
buy milk
Lock.release()
thread B
Lock.acquire()
if (no milk)
buy milk
Lock.release()
10
Implementing Locks
Requires hardware support (in general)
Can build on atomic operations: Disable interrupts
Uniprocessors only Test & Set, Compare & Swap
11
Disabling Interrupts
Prevent scheduler from switching threads in middle of critical sections Ignores quantum expiration (timer
interrupt) No handling I/O operations
(Don’t make I/O calls in critical section!)
To ensure current sequence of instructions run atomically
Drawback?
12
Atomic Read-Write-Modify Instructions Atomically read old value, write
new value
Examples: Test & Set (most arch) Exchange (x86) Compare & Swap (68K, Sparc)
13
Implementing Locks: Test & Set
int testset (int value) { int old = value; value = 1; return old;}
pseudo-code: red = atomic
class Lock { private int value; Lock() { value = 0; } void acquire() {…} void release() {…}}
void acquire() { while (testset(value)) {}}
void release() { value = 0;}
Effect of testset(value) when value = 0? or 1?
acquire – wait until lock released, then take it
release – release lock value: 1 (locked); 0
(unlocked)
14
Busy Waiting (“Spinning”)
What’s wrong with this implementation? CPU utilization? Different
priorities?
void acquire() { while (testset(value))
{}}
void release() { value = 0;}
spin-lock
15
Minimizing Busy Waiting
Can’t implement locks with test & set without any waiting (w/o disabling interrupts) Add queue to lock and sleep: blocking
lockvoid acquire() { while (1) { if (testset(value)) { put thread on queue, sleep } else { break; } } }
void release() { value = 0; wake up threads;}
16
Locks in POSIX
pthread_mutex_t my_mutex;pthread_mutex_init(&my_mutex);// also: pthread_mutex_t my_mutex = PTHREAD_MUTEX_INITIALIZER;...pthread_mutex_lock(&my_mutex);x = x + 1; /* This is the critical section */pthread_mutex_unlock(&my_mutex);...pthread_mutex_destroy(&my_mutex);
pthread_mutex_t my_mutex;pthread_mutex_init(&my_mutex);// also: pthread_mutex_t my_mutex = PTHREAD_MUTEX_INITIALIZER;...pthread_mutex_lock(&my_mutex);x = x + 1; /* This is the critical section */pthread_mutex_unlock(&my_mutex);...pthread_mutex_destroy(&my_mutex);
17
Locks in POSIX: Example
Reader-writer problem: Share a buffer which holds one item (an
integer) A single reader and writer Reader: only read when there is item in
buffer Writer: only write when there is space in
buffer
18
Locks in POSIX: Example - II#include <pthread.h>
#include <stdio.h>
int buffer_has_item = 0;
int buffer;
pthread_mutex_t mutex;
void * reader_function(void *)
{
while(1){
pthread_mutex_lock( &mutex );
if ( buffer_has_item == 1) {
printf("reader consumes one item: %d.\n", buffer);
buffer_has_item = 0;
}
pthread_mutex_unlock( &mutex );
}
}
19
Locks in POSIX: Example - IIIvoid writer_function(void)
{
while(1) {
pthread_mutex_lock( &mutex );
if ( buffer_has_item == 0 ){
buffer = rand()*100;
printf("writer produces one item: %d\n", buffer);
buffer_has_item = 1;
}
pthread_mutex_unlock( &mutex );
}
}
void main()
{
pthread_t reader;
pthread_mutex_init(&mutex, NULL);
pthread_create( &reader, NULL, reader_function,NULL);
writer_function();
}
20
Locks as Synch Primitive
+ Locks provide mutual exclusion+ Only one thread enters section at a time+ Simplifies writing concurrent programs
- Low-level- Can complicate coding
- e.g., “allow at most n threads to access resource”
What are some alternatives?
21
Locks & Semaphores
Implementing locks Semaphores Monitors
22
Semaphores
What’s a “semaphore” anyway?
A visual system for sending information by means of two flags that are held one in each hand, using an alphabetic code based on the position of the signaler's arms.
23
Semaphores
What’s a “semaphore” anyway?
A visual signaling apparatus with flags, lights, or mechanically moving arms.
24
Semaphores in CS
Computer science: Dijkstra (1965)
A non-negative integer counter with atomic increment & decrement. Blocks rather than going negative. Higher-level than locks but not too high level
25
Semaphores: Key Concepts
P(sem), a.k.a. wait = decrement counter
If sem = 0, block until greater than zero
P = “prolagen” (proberen te verlagen, “try to decrease”)
In Holland the good Dr. DijkstraTook a break from a walk on his bijkstra
And said: "Which shall it be?Take a P or a V?
For the two seem to me quite alijkstra!"
V(sem), a.k.a. signal = increment counter
Wake 1 waiting process
V = “verhogen” (“increase”)
26
Implementing Semaphores
class Semaphore { private int value; private Queue q; Semaphore(int v) { value = v; }}
void wait() { if (value > 0) { value = value – 1; }
if (value == 0) { add this process to Q; sleep(); } }
void signal() { value = value + 1; if (anyone on Q) { remove P from Q; wakeup(P); } }
27
Variants of Semaphores Binary semaphore
just two values (0 or 1), typically initial value 1 (“free”)
Counting semaphore useful when units of resource are
available initialized to number of resources thread can access as long as one
unit available
28
Binary Semaphores: Example
“too much milk” with locks
thread A
Lock.acquire()
if (no milk)
buy milk
Lock.release()
thread B
Lock.acquire()
if (no milk)
buy milk
Lock.release()
thread A
sem.wait()
if (no milk)
buy milk
sem.signal()
thread B
sem.wait()
if (no milk)
buy milk
sem.signal()
“too much milk” with binary semaphores(initially 1)
29
Binary Semaphore: Example
More flexible than locks! By initializing semaphore to 0,
threads can wait for an event to occur
thread A
// wait for thread B
sem.wait();// do stuff …
thread B
// do stuff, then
// wake up A
sem.signal();
30
Counting Semaphores: Example
Controlling resources: Allow threads to use at most 5 files simultaneously
Initialize to 5
thread A
sem.wait();// use a file
sem.signal();
thread B
sem.wait();// use a file
sem.signal();
31
Semaphore in POSIX
sem_t sem;sem_init(&sem, int pshared, unsigned value);
sem_wait(&sem);sem_post(&sem);
sem_destroy(&sem);
sem_t sem;sem_init(&sem, int pshared, unsigned value);
sem_wait(&sem);sem_post(&sem);
sem_destroy(&sem);
32
Solving Reader-writer Problem Using Semaphore
Reader-writer problem: Share a buffer which holds one item (an
integer) A single reader and writer Reader: only read when there is item in
buffer Writer: only write when there is space
in buffer How to make the writer enter the
critical section first? How to make the reader & writer
alternate to enter critical section?
33
Semaphore in Linux: Solving Reader-writer Problem#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
sem_t readers_turn, writers_turn; /* semaphore declaration */
int buffer, loopcnt = 5;
void * reader_function(void *)
{
int i;
for (i=0; i<loopcnt; i++) {
sem_wait( &readers_turn );
printf("reader consumes one item: %d.\n", buffer);
sem_post( &writers_turn );
}
}
void writer_function(void) {
int i;
for (i=0; i<loopcnt; i++) {
sem_wait( &writers_turn );
buffer = rand();
printf("writer produces one item: %d\n", buffer);
sem_post( &readers_turn );
}
}
34
Semaphore in Linux: Solving Reader-writer Problem - IIint main()
{
pthread_t reader;
if (sem_init(&readers_turn, 0, 0) < 0) {
perror("sem_init");
exit(1);
}
if (sem_init(&writers_turn, 0, 1) < 0) {
perror("sem_init");
exit(1);
}
if(pthread_create( &reader, NULL, reader_function, NULL) != 0) {
perror("pthread_create");
exit(1);
}
writer_function();
sem_destroy(&readers_turn);
sem_destroy(&writers_turn);
return 1;
}
35
Summary
Implementing locks Test & Set Spin locks, blocking locks
Semaphores Generalization of locks Binary, counting (“Dijkstra-style”) Useful for:
Controlling resources Waiting on events