[os] Pthread: Synchronization, Deadlock , Semaphore

Synthronization

  • mutual exclusion:

"dekker's algorithm": Dekker's algorithm is the first known correct solution to the mutual exclusion problem in concurrent programming.

 /* global or shared memory */
                    int x = 5;
                    int needLockT1 = 0;   /* 0 or 1 */
                    int needLockT2 = 0;   /* 0 or 1 */
                    int turn = T1;        /* T1 or T2 */

  /* Thread T1 */                      /* Thread T2 */
  while ( 1 )                              while ( 1 )
  {                                            {
    execNonCriticalSection();            execNonCriticalSection();
    needLockT1 = 1;                          needLockT2 = 1;
    turn = T2;                                       turn = T1;                 // compete for the 'turn'

    while ( turn == T2 &&                while ( turn == T1 &&
            needLockT2 == 1 )                    needLockT1 == 1 )
    {                                            {
      /* busy wait */                              /* busy wait */
    }                                            }

    execCriticalSection();               execCriticalSection();
    needLockT1 = 0;                      needLockT2 = 0;
  }                                                }


T1: needLockT1 = 1;
T1: turn = T2;
T2: needLockT2 = 1;
T2: turn = T1;
T1: execCriticalSection();
T1: needLockT1 = 0;
T2: execCriticalSection();

Semaphore:

"a system of sending messages by holding the arms or two flags or poles in certain positions according to an alphabetic code."

-- an OS construct that enables us to have synchronized access
to one or more shared resources

-- special non-negative int variable

-- two operations:

(1) first operation essentially attempts to gain access

   P()     proberen (to try)
   wait()
   down()

(2) second operation relinquishes the access the acquired

   V()     vrijgeven (to release)
   signal()
   up()

semaphore S is non-negative int variable

  P( semaphore S )                         /* this P() operation MUST execute without    */
  {                                                    /*  any interruption, i.e., no context switch */
    while ( S == 0 ) /* resource count*/          /*   between exiting the while() loop and     */
    {                                              /*    executing S--                           */
      /* busy wait */
    }
    S--;
  }

  V( semaphore S )
  {
    S++;
  }
  • the "producer/consumer" problem (a.k.a. shared buffer problem)

-- Given a shared buffer (i.e., array) of a fixed size n
-- One or more producer threads
-- One or more consumer threads

                        /* shared/global memory */
                        int n = 20;
                        buffer[n];
                        semaphore empty_slots = n;
                        semaphore used_slots = 0;
                        semaphore mutex = 1;

  /* producer */                           /* consumer */
  while ( 1 )                              while ( 1 )
  {                                        {
    item = produce_next_item();              P( used_slots );
    P( empty_slots );                          P( mutex );
      P( mutex );                                item = remove_from_buffer();
        add_to_buffer( item );                 V( mutex );
      V( mutex );                            V( empty_slots );
    V( used_slots );                         consume( item );
  }                                        }

uses two counting semaphores to ensure:
(1) no buffer overflow will occur in a producer
(2) no reading from an empty buffer in the consumer

Uses mutex to ensure the add/remove could be exclusively happening at the same time.

"A Mutex is different than a semaphore as it is a locking mechanism while a semaphore is a signalling mechanism. A binary semaphore can be used as a Mutex but a Mutex can never be used as a semaphore."

  • DINING PHILOSOPHERS PROBLEM

Given: five philosophers that engage in only two activities:
-- thinking (i.e., independent computation)
-- eating (i.e., sharing a resource; therefore, requires synchronization)
Given: shared table with five bowls and five chopsticks,
and a bowl of food in the middle of the table
(which is endlessly replenished)
Key contraint: to eat(), a philosopher must obtain two chopsticks,
one from the left, one from the right

First attempt:

chopstick is array[5] of semaphores

  philosopher( i )     /* i in 0..4 */
  {
    while ( 1 )
    {
      think()
      P( chopstick[i] )   //DEADLOCK
        P( chopstick[i+1%5] )
          eat()                 /* critical section */
        V( chopstick[i+1%5] )
      V( chopstick[i] )
    }
  }

Second attempt:

chopstick is array[5] of semaphores

  philosopher( i )     /* i in 0..4 */
  {
    while ( 1 )
    {
      think()
      P( mutex );  // top-level mutex -- NOT EFFICIENT
        P( chopstick[i] )
        P( chopstick[i+1%5] )
      V( mutex );
      eat()                 /* critical section */
      V( chopstick[i+1%5] )
      V( chopstick[i] )
    }
  }

Third attempt:

-- use an asymmetric solution

chopstick is array[5] of semaphores

  philosopher( i )     /* i in 0..3 (instead of i in 0..4) */
  {
    while ( 1 )
    {
      think()
      P( chopstick[i] )
        P( chopstick[i+1%5] )
          eat()                 /* critical section */
        V( chopstick[i+1%5] )
      V( chopstick[i] )
    }
  }

  philosopher( i )     /* i is always 4 */    /*it holds the slots required as the "left" chopsticks for both its nbrs*/
  {
    while ( 1 )
    {
      think()
      P( chopstick[i+1%5] )    /* we swapped the order of the P() operations */
        P( chopstick[i] )
          eat()                 /* critical section */
        V( chopstick[i] )
      V( chopstick[i+1%5] )
    }
  }

Deadlock

Deadlock: We have deadlock when no process/thread can make any
further progress (i.e., all blocked on P() operation
and the given resource will NEVER become available)

Deadlock requires four conditions:
-- mutual exclusion
-- hold and wait
-- no preemption
-- circular wait -- i.e., a cycle in resource allocation graph

Deadlock:

  1. "classical circular"

  2. circular: P1, P2, R1, R2

posted @ 2019-03-25 23:01  gooey  阅读(157)  评论(0编辑  收藏  举报