Lock, semaphore, condition variables and monitors.

来源:互联网 发布:网络侦探杰斯兽 编辑:程序博客网 时间:2024/06/01 10:09
How to pick between locks, semaphores, condvars, monitors?
    Locks are very simple for many cases(spinlock is used in kernel space for very simple content.)
        Issues: Maybe not the most efficient solution.
            For example, can't allow multiple readers but one writer inside a standard lock.
    Condition variables allow threads to sleep while holding a lock.
        Just be sure you understand whether they use Mesa(re-check condition when wakes up) or Hoare semntics.
Checks wait condition first, sends necessary signal at the end of procedure.
    Semaphores provide pretty general functionality
        But also make it really easy to botch things up.
    Monitors(e.g. synchronized in Java) are a "pattern" for using locks and condition variables that is often very useful.


Semaphore:
    Down operation on a semaphore checks to see if the value is greater than 0, if so, it decrements the value and just continues.
    If the value is 0, the process is put to sleep(sleeping queue) without completing the down for the moment.
    
    Up operation increments the value of the semaphore addressed, if one or more process were sleeping on that semaphore, unable to complete an earlier down operation, one of them is chosen by the system and is allowed to complete its down.
    Thus, after an up on a semaphore with processes sleeping on it, the operation of incrementing the semaphore and waking up one process is also indivisible.
    No process ever blocks doing an up.
    Binary semaphore essentially the same as a lock.

    Simple semaphore implementation
   
 struct semaphore {        int val;        thread_list waiting; //list of threads waiting for semaphore.    };    Down(sem):        while(sem.val <= 0)    <span style="color:#ff0000;">//Another thread might call Down() while this thread is blocked, so we have to check the condition again when we wake back up. Don't use if statement at here.</span>            sem.waiting.append(thread);            block(thread);        sem.val -= 1;        return;    Up(sem):        sem.val += 1;        if(!sem.waiting.empty())            thread = sem.waiting.remove();            wakeup(thread);

    Example of producer/consumer:
    
semaphore mutex = 1;    //init value is very important.    semaphore empty = N;    semaphore full = 0;    producer()        while(true)            item = produce_item()            down(&empty)    //blocked process adds to sleeping queue            down(&mutex)            insert_item()            up(&mutex)            up(&full)    consumer()        while(true)            down(&full)            down(&mutex)            item = remove_item()            insert_item()            up(&mutex)            up(&empty)    //wake up sleeping processes from sleeping queue random, never block process.            consume_item(item);


    Reader/Writer
   
    semaphore mutex = 1;    //protect readcount.    semaphore wrt = 1;    int    readcount = 0;    writer():        Down(wrt)        do_write()        Up(wrt)    Reader():        Down(mutex)        if (++readcount == 1)            Down(wrt)        Up(mutex)        do_read()        Down(mutex)        if (--readcount == 0)            Up(wrt)        Up(mutex)


    Futex:     
        fast user space mutex, avoid context swtich(expensive system call) until it is needed. 
        kernel service provides a "wait queue" that allows multiple processes to wait on a lock. They will not run, unless the kernel explicitly unblocks them.
        kernel blocks threads until lock is available to avoid context switch.
        processs share a common lock variable(32-bit integer).

    Critical section(region):
        Section that the shared memory is accessed.

    Mutex:
        mutex is a shared variable(per-process) that is used to synchronize threads and protected critical region.

    Condition Mutex:
        Synchronization mechanism allows threads to block due to some condition not being met. Almost always is used with mutex.
        The pattern is for one thread to lock a mutex, then wait on a conditional variable when it cannot get what it needs. 
        pthread_cond_wait call atomically unlocks the mutex it is holding.
        condition variables(unlike semaphore) have no memory, signals may be lost(it's better to use broadcast to wakeup threads).
        Synchronization variable that can be signaled to tell the thread that the resource is available to avoid busy waiting.

    
Key ideas for CV:
    wait() on a CV releases the lock
    signal on a CV wakes up a thread waiting on a lock
    The thread that wakes up has to re-lock before wait returns then  recheck condition. 

    Semaphores integrate the state into atomic P/V primitives, but the only state that is supported is a simple counter.
    Condition variables(CVs) allow the program to define the condition/state, and t protect it with an integrated mutex.

    Semaphores vs. Condition Variables
    1. Up differs from Signal in that:
        1). Signal has no effect if no thread is waiting on the condition(lost). 
        Condition variables are not variables, they have no value.
        2). Up has the same effect whether or not a thread is waiting.
        Semaphores retain a "memory" of calls to Up.
    2. Down differs from Wait in that:
        1). Down checks the condition and blocks only if necessary.
        No need to recheck the condition after returning from Down wait condition is defined internally, but is limited to a counter.
        2). Wait is explicit: it does not check the condition, ever.
        condition is defined externally and protected by intergrated mutex.

    Semaphores using Condition variables.
    void Down() {
        mutex->acquire();
        assert(count >= 0);
        while(count == 0)
            condition->wait(mutex);
        count -= 1;
        mutex->release();
    }

    void Up() {
        mutex->acquire();
        count += 1;
        condition->signal(mutex);
        mutex->release();
    }
        Condition variable: one or more threads waiting on a single particular condition(e.g. for queue to be empty)
        Semaphores are good for producer/consumer situations. Shared resource that can be availabe or unavailable based on some integer number of available things.

        
    Monitors:
    The style of using locks and CV's to protect access to a shared object is often called a monitor.


0 0
原创粉丝点击