Synchronization & Deadlock

Part 1: Mutex Locks

Critical Section

When thread process the variable. There are three steps:bash

  • 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.ide

mutex_lock

We can use pthread_mutex_lock() to avoid the critical section.oop

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.post

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:ui

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.spa

  • spurious wake exist for performance.

pthread_cond_wait

  1. unlock the mutex
  2. wait until pthread_cond_signal is called
  3. 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().code

Write our own simple barrier:orm

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.
    1. 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.three

2. What problem does the Banker's Algorithm solve?

Deadlockip

3. What is the difference between Deadlock Prevention, Deadlock Detection and Deadlock Avoidance?

4. Sketch how to use condition-variable based barrier to ensure your main game loop does not start until the audio and graphic threads have initialized the hardware and are ready.

5. Implement a producer-consumer fixed sized array using condition variables and mutex lock.

6. Create an incorrect solution to the CSP for 2 processes that breaks: i) Mutual exclusion. ii) Bounded wait.

7. Create a reader-writer implementation that suffers from a subtle problem. Explain your subtle bug.

相關文章
相關標籤/搜索