Part 1: Mutex Locks
Critical Section
When thread process the variable. There are three steps:
- 1 Copy the variable from memory to CPU
- 2 Do some calculation
- 3 Store the new value back to memory
If multiple thread is executing same variable at the same time, there might be a critical section.
mutex_lock
We can use pthread_mutex_lock() to avoid the critical section.
pthread_mutex_t *lock = malloc(sizeof(pthread_mutex_t);
pthread_mutex_init(lock,NULL);
//same as pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER
pthread_mutex_lock(lock);
pthread_mutex_unlock(lock);
pthread_mutex_destroy(lock);
free(lock);
复制代码
Pthread_mutex_lock won't stop other threads, other threads will have to wait when they are trying to lock the same lock until the mutex is unlocked. If a mutex was locked, it can't be unlock by another thread.
Gotchas
- Multiple threads init/destroy has undefined behavior
- Destroying a locked mutex has undefined behavior
- Forgot to unlock will lead to deadlock
- Can't copy pthread_mutex_t to a new memory location
- Use uninitialized mutex
- Not calling destroy lead to resource leak
- Locking/ unlocking wrong mutex
Part 2: Counting Semaphores
sem_t s;
sem_init(&s, 0, 10);
sem_wait(&s);
sem_post(&s);
sem_destroy(&s);
复制代码
Thread Safe Push:
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
int count = 0;
double values[10];
sem_t sitems, sremain;
void init(){
sem_init(&sitems, 0, 0);
sem_init(&sremains, 0, 10);
}
double pop(){
sem_wait(&sitems);
pthread_mutex_lock(&m);
double result = values[--count];
pthread_mutex_unlock(&m);
sem_post(&sremains);
return result;
}
void push(double v){
sem_wait(&sremains);
pthread_mutex_lock(&m);
values[count++] = v;
pthread_mutex_unlock(&m);
sem_post(&sitems);
}
}
复制代码
Three desired properties for solutions to the critical section problem
- Mutual Exculsion: Only one thread/process get access
- Progress: If there is no progress inside critical section, thread should be able to get inside without waiting
- Bounded: The wait time should be finite
The Critical Section Problem
raise my flag
turn = your_id
wait while your flag is raised and turn is your_id
//Do critical Section
lower my flag
复制代码
Part 3: Condition Variables
What is condition variable
It allow threads sleep until tickled. We can use pthread_cond_signal() to wake up one thread (System decide which one to wake up) or all threads with pthread_cond_broadcast(). There will be a spurious wake of waiting thread.
- spurious wake exist for performance.
pthread_cond_wait
- unlock the mutex
- wait until pthread_cond_signal is called
- lock before return
Use condition number to write semaphore
define struct sem_t{
int count;
pthread_mutex_t m;
pthread_cond_t cv;
} sem_t
int sem_init(sem_t *s, int pshared, int value){
if(pshared){errno = ENOSYS;return -1;}
s->count = 0;
pthread_mutex_init(s->&m, NULL);
pthread_cond_init(s->&cv, NULL);
return 0;
}
void sem_wait(sem_t *s){
pthread_mutex_lock(s->&m);
while(s->count == 0){
pthread_cond_wait(&cv,&m);
}
s->count--;
pthread_mutex_unlock(s->&m);
}
void sem_signal(){
pthread_mutex_lock(s->&m);
count++;
pthread_cond_signal(s->&cv);
pthread_mutex_unlock(s->&m);
}
复制代码
Part 4: Barrier
Barrier is used to wait N threads to reach a certain point before continuing onto next step. There is a function named pthread_barrier_wait(). We need to declare a pthread_barrier_t variable and initialize it with pthread_barrier_init().
Write our own simple barrier:
pthread_mutex_lock(&m);
remain --;
if(remain == 0){
pthread_cond_broadcast(&cv);
}
while(remain > 0){
pthread_cond_wait(&cv,&m);
}
pthread_mutex_unlock(&m);
复制代码
Part 5: Reader/Writer problen
reader(){
lock(&m);
while(writer) pthread_cond_wait(&cv, &m);
read++
unlock(&m);
lock(&m)
read--;
pthread_cond_broadcast(&cv);
unlock(&m);
}
writer(){
lock(&m);
writer++;
while(writer || reader) pthread_cond_wait(&cv,&m);
writer++;
unlock(&m);
lock(&m);
writer--;
writer--;
pthread_cond_broadcast(&cv);
unlock(&m);
}
复制代码
Deadlcok
Coffman conditions
- Mutex Exclusion: The resource cannot be shared
- Circular Wait: There exists a cycle in the Resource Allocation Graph
- Hold and Wait: Holding some resource and waiting for the resource holded by others
- No pre-emption: The resource can't be taken away by other thread
Dining Philosophers
- Left-right Deadlock will lead to circle wait. Hold left and wait for right.
- Trylock? will cause a livelock.
-
- Arbitrator 2. Leave table 3. All choose lower
Exam Practice
1. Define circular wait, mutual exclusion, hold and wait, and no-preemption. How are these related to deadlock?
circular wait: There exist a circle in Resource Allocation Graph. hold and wait: Thread will holding the current resource while waiting for the resource hold by others mutual exclusion: The resource can't be shared no-preemption: Resource can't be taken by others.
2. What problem does the Banker's Algorithm solve?
Deadlock