Saturday, January 17, 2015

IPC: mutex vs conditional variable or semaphore

what is lock and conditional variable:


  • Condition variables provide yet another way for threads to synchronize. While mutexes implement synchronization by controlling thread access to data, condition variables allow threads to synchronize based upon the actual value of data.
  • Without condition variables, the programmer would need to have threads continually polling (possibly in a critical section), to check if the condition is met. This can be very resource consuming since the thread would be continuously busy in this activity. A condition variable is a way to achieve the same goal without polling.
  • A condition variable is always used in conjunction with a mutex lock. 
why we need conditional variable or semaphore instead of mutex lock:

  • Locks are used for mutual exclusion. When you want to ensure that a piece of code is atomic, put a lock around it. You could theoretically use a binary semaphore to do this, but that's a special case.
  • Semaphores and condition variables build on top of the mutual exclusion provide by locks and are used for providing synchronized access to shared resources. They can be used for similar purposes.
  • A condition variable is generally used to avoid busy waiting (looping repeatedly while checking a condition) while waiting for a resource to become available. For instance, if you have a thread (or multiple threads) that can't continue onward until a queue is empty, the busy waiting approach would be to just doing something like:
  • //pseudocode
    while(!queue.empty())
    {
       sleep(1);
    }
    
  • The problem with this is that you're wasting processor time by having this thread repeatedly check the condition. Why not instead have a synchronization variable that can be signaled to tell the thread that the resource is available?
  • //pseudocode
    syncVar.lock.acquire();
    
    while(!queue.empty())
    {
       syncVar.wait();
    }
    
    //do stuff with queue
    
    syncVar.lock.release();
    
  • Presumably, you'll have a thread somewhere else that is pulling things out of the queue. When the queue is empty, it can call syncVar.signal() to wake up a random thread that is sitting asleep on syncVar.wait() (or there's usually also a signalAll() or broadcast() method to wake up all the threads that are waiting).
  • I generally use synchronization variables like this when I have one or more threads waiting on a single particular condition (e.g. for the queue to be empty).
  • Semaphores can be used similarly, but I think they're better used when you have a shared resource that can be available and unavailable based on some integer number of available things. Semaphores are good for producer/consumer situations where producers are allocating resources and consumers are consuming them.
  • Think about if you had a soda vending machine. There's only one soda machine and it's a shared resource. You have one thread that's a vendor (producer) who is responsible for keeping the machine stocked and N threads that are buyers (consumers) who want to get sodas out of the machine. The number of sodas in the machine is the integer value that will drive our semaphore.
  • Every buyer (consumer) thread that comes to the soda machine calls the semaphore down() method to take a soda. This will grab a soda from the machine and decrement the count of available sodas by 1. If there are sodas available, the code will just keep running past the down() statement without a problem. If no sodas are available, the thread will sleep here waiting to be notified of when soda is made available again (when there are more sodas in the machine).
  • The vendor (producer) thread would essentially be waiting for the soda machine to be empty. The vendor gets notified when the last soda is taken from the machine (and one or more consumers are potentially waiting to get sodas out). The vendor would restock the soda machine with the semaphore up() method, the available number of sodas would be incremented each time and thereby the waiting consumer threads would get notified that more soda is available.
  • The wait() and signal() methods of a synchronization variable tend to be hidden within the down() and up() operations of the semaphore.
  • Certainly there's overlap between the two choices. There are many scenarios where a semaphore or a condition variable (or set of condition variables) could both serve your purposes. Both semaphores and condition variables are associated with a lock object that they use to maintain mutual exclusion, but then they provide extra functionality on top of the lock for synchronizing thread execution. It's mostly up to you to figure out which one makes the most sense for your situation.
  • That's not necessarily the most technical description, but that's how it makes sense in my head.
  • reference: 
http://stackoverflow.com/questions/3513045/conditional-variable-vs-semaphore 



Difference between mutex and semaphore:
https://techdifferences.com/difference-between-semaphore-and-mutex.html



Example for mutex:

Example: Using Mutexes

    Example Code - Using Mutexes
        This example program illustrates the use of mutex variables in a threads program that performs a dot product. The main data is made available to all threads through a globally accessible structure. Each thread works on a different part of the data. The main thread waits for all the threads to complete their computations, and then it prints the resulting sum.

    #include <pthread.h>
    #include <stdio.h>
    #include <stdlib.h>

    /*  
    The following structure contains the necessary information 
    to allow the function "dotprod" to access its input data and
    place its output into the structure. 
    */

    typedef struct
     {
       double      *a;
       double      *b;
       double     sum;
       int     veclen;
     } DOTDATA;

    /* Define globally accessible variables and a mutex */

    #define NUMTHRDS 4
    #define VECLEN 100
       DOTDATA dotstr;
       pthread_t callThd[NUMTHRDS];
       pthread_mutex_t mutexsum;

    /*
    The function dotprod is activated when the thread is created.
    All input to this routine is obtained from a structure
    of type DOTDATA and all output from this function is written into
    this structure. The benefit of this approach is apparent for the
    multi-threaded program: when a thread is created we pass a single
    argument to the activated function - typically this argument
    is a thread number. All  the other information required by the
    function is accessed from the globally accessible structure.
    */

    void *dotprod(void *arg)
    {

       /* Define and use local variables for convenience */

       int i, start, end, len ;
       long offset;
       double mysum, *x, *y;
       offset = (long)arg;
        
       len = dotstr.veclen;
       start = offset*len;
       end   = start + len;
       x = dotstr.a;
       y = dotstr.b;

       /*
       Perform the dot product and assign result
       to the appropriate variable in the structure.
       */

       mysum = 0;
       for (i=start; i<end ; i++)
        {
          mysum += (x[i] * y[i]);
        }

       /*
       Lock a mutex prior to updating the value in the shared
       structure, and unlock it upon updating.
       */
       pthread_mutex_lock (&mutexsum);
       dotstr.sum += mysum;
       pthread_mutex_unlock (&mutexsum);

       pthread_exit((void*) 0);
    }

    /*
    The main program creates threads which do all the work and then
    print out result upon completion. Before creating the threads,
    the input data is created. Since all threads update a shared structure,
    we need a mutex for mutual exclusion. The main thread needs to wait for
    all threads to complete, it waits for each one of the threads. We specify
    a thread attribute value that allow the main thread to join with the
    threads it creates. Note also that we free up handles when they are
    no longer needed.
    */

    int main (int argc, char *argv[])
    {
       long i;
       double *a, *b;
       void *status;
       pthread_attr_t attr;

       /* Assign storage and initialize values */
       a = (double*) malloc (NUMTHRDS*VECLEN*sizeof(double));
       b = (double*) malloc (NUMTHRDS*VECLEN*sizeof(double));
     
       for (i=0; i<VECLEN*NUMTHRDS; i++)
        {
         a[i]=1.0;
         b[i]=a[i];
        }

       dotstr.veclen = VECLEN;
       dotstr.a = a;
       dotstr.b = b;
       dotstr.sum=0;

       pthread_mutex_init(&mutexsum, NULL);
            
       /* Create threads to perform the dotproduct  */
       pthread_attr_init(&attr);
       pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

        for(i=0; i<NUMTHRDS; i++)
            {
        /*
        Each thread works on a different set of data.
        The offset is specified by 'i'. The size of
        the data for each thread is indicated by VECLEN.
        */
        pthread_create(&callThd[i], &attr, dotprod, (void *)i);
        }

         pthread_attr_destroy(&attr);

            /* Wait on the other threads */
        for(i=0; i<NUMTHRDS; i++)
            {
          pthread_join(callThd[i], &status);
        }

       /* After joining, print out the results and cleanup */
       printf ("Sum =  %f \n", dotstr.sum);
       free (a);
       free (b);
       pthread_mutex_destroy(&mutexsum);
       pthread_exit(NULL);
    }  



Example 1: Using Condition Variables

    Example Code - Using Condition Variables
        This simple example code demonstrates the use of several Pthread condition variable routines. The main routine creates three threads. Two of the threads perform work and update a "count" variable. The third thread waits until the count variable reaches a specified value.

    #include <pthread.h>
    #include <stdio.h>
    #include <stdlib.h>

    #define NUM_THREADS  3
    #define TCOUNT 10
    #define COUNT_LIMIT 12

    int     count = 0;
    int     thread_ids[3] = {0,1,2};
    pthread_mutex_t count_mutex;
    pthread_cond_t count_threshold_cv;

    void *inc_count(void *t)
    {
      int i;
      long my_id = (long)t;

      for (i=0; i<TCOUNT; i++) {
        pthread_mutex_lock(&count_mutex);
        count++;

        /*
        Check the value of count and signal waiting thread when condition is
        reached.  Note that this occurs while mutex is locked.
        */
        if (count == COUNT_LIMIT) {
          pthread_cond_signal(&count_threshold_cv);
          printf("inc_count(): thread %ld, count = %d  Threshold reached.\n",
                 my_id, count);
          }
        printf("inc_count(): thread %ld, count = %d, unlocking mutex\n",
           my_id, count);
        pthread_mutex_unlock(&count_mutex);

        /* Do some "work" so threads can alternate on mutex lock */
        sleep(1);
        }
      pthread_exit(NULL);
    }

    void *watch_count(void *t)
    {
      long my_id = (long)t;

      printf("Starting watch_count(): thread %ld\n", my_id);

      /*
      Lock mutex and wait for signal.  Note that the pthread_cond_wait
      routine will automatically and atomically unlock mutex while it waits.
      Also, note that if COUNT_LIMIT is reached before this routine is run by
      the waiting thread, the loop will be skipped to prevent pthread_cond_wait
      from never returning.
      */
      pthread_mutex_lock(&count_mutex);
      while (count<COUNT_LIMIT) {
        pthread_cond_wait(&count_threshold_cv, &count_mutex);
        printf("watch_count(): thread %ld Condition signal received.\n", my_id);
        count += 125;
        printf("watch_count(): thread %ld count now = %d.\n", my_id, count);
        }
      pthread_mutex_unlock(&count_mutex);
      pthread_exit(NULL);
    }

    int main (int argc, char *argv[])
    {
      int i, rc;
      long t1=1, t2=2, t3=3;
      pthread_t threads[3];
      pthread_attr_t attr;

      /* Initialize mutex and condition variable objects */
      pthread_mutex_init(&count_mutex, NULL);
      pthread_cond_init (&count_threshold_cv, NULL);

      /* For portability, explicitly create threads in a joinable state */
      pthread_attr_init(&attr);
      pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
      pthread_create(&threads[0], &attr, watch_count, (void *)t1);
      pthread_create(&threads[1], &attr, inc_count, (void *)t2);
      pthread_create(&threads[2], &attr, inc_count, (void *)t3);

      /* Wait for all threads to complete */
      for (i=0; i<NUM_THREADS; i++) {
        pthread_join(threads[i], NULL);
      }
      printf ("Main(): Waited on %d  threads. Done.\n", NUM_THREADS);

      /* Clean up and exit */
      pthread_attr_destroy(&attr);
      pthread_mutex_destroy(&count_mutex);
      pthread_cond_destroy(&count_threshold_cv);
      pthread_exit(NULL);

    }

output:
Starting watch_count(): thread 1
inc_count(): thread 2, count = 1, unlocking mutex
inc_count(): thread 3, count = 2, unlocking mutex
watch_count(): thread 1 going into wait...
inc_count(): thread 3, count = 3, unlocking mutex
inc_count(): thread 2, count = 4, unlocking mutex
inc_count(): thread 3, count = 5, unlocking mutex
inc_count(): thread 2, count = 6, unlocking mutex
inc_count(): thread 3, count = 7, unlocking mutex
inc_count(): thread 2, count = 8, unlocking mutex
inc_count(): thread 3, count = 9, unlocking mutex
inc_count(): thread 2, count = 10, unlocking mutex
inc_count(): thread 3, count = 11, unlocking mutex
inc_count(): thread 2, count = 12  Threshold reached. Just sent signal.
inc_count(): thread 2, count = 12, unlocking mutex
watch_count(): thread 1 Condition signal received.
watch_count(): thread 1 count now = 137.
inc_count(): thread 3, count = 138, unlocking mutex
inc_count(): thread 2, count = 139, unlocking mutex
inc_count(): thread 3, count = 140, unlocking mutex
inc_count(): thread 2, count = 141, unlocking mutex
inc_count(): thread 3, count = 142, unlocking mutex
inc_count(): thread 2, count = 143, unlocking mutex
inc_count(): thread 3, count = 144, unlocking mutex
inc_count(): thread 2, count = 145, unlocking mutex
Main(): Waited on 3 threads. Final value of count = 145. Done.

Example2:

here critical and global data is event variable





in ethernet world, pkt may come at any time. so to a pkt one thread was waiting(pktrcv thread). and if pkt received (TA,TB,TC) threads will do some actions. so untill pkt received TA,TB,TC went to conditional wait state. if pkt received those pktrcv thread wakes up the TA,TB,TC thread and then it process that common pkt buffer global data structures using mutex lock(may be TA,TB,TC are waken up parallely). so before global pkt buffer data strucutre we need a mutex lock.

say thread1 and thread 2 and thread 3 is waiting for an event,
so those thread will unlock the mutex anevend go for the cond wiat. say thread 4 taking mutex lock and sending the signal to waiting thread2 . Among that thread2 taking lock and checking whether event is recived. if yes it note that event and unset that event and return success. so thread 2 do action based on the rx event and again waiting for any new event.


Reference:
https://computing.llnl.gov/tutorials/pthreads/#Exercise1



What is lock and what is semaphore?

As per operating system terminology, the mutex and semaphore are kernel resources that provide synchronization services (also called as synchronization primitives). Why do we need such synchronization primitives? Won’t be only one sufficient?

A semaphore is a generalized mutex. In lieu of single buffer, we can split the 4 KB buffer into four 1 KB buffers (identical resources). A semaphore can be associated with these four buffers. The consumer and producer can work on different buffers at the same time.


Differnce between lock and semaphore:

a mutex is locking mechanism used to synchronize access to a resource. Only one task (can be a thread or process based on OS abstraction) can acquire the mutex. It means there will be ownership associated with mutex, and only the owner can release the lock (mutex)

 A semaphore is a generalized mutex. In lieu of single buffer, we can split the 4 KB buffer into four 1 KB buffers (identical resources). A semaphore can be associated with these four buffers. The consumer and producer can work on different buffers at the same time.


Reference:

https://computing.llnl.gov/tutorials/pthreads/#Exercise1

http://www.geeksforgeeks.org/mutex-vs-semaphore/
see.stanford.edu/materials/icsppcs107/23-Concurrency-Examples.pdf
http://koti.mbnet.fi/niclasw/MutexSemaphore.html
http://www.scs.stanford.edu/15wi-cs140/notes/
https://jlmedina123.wordpress.com/2013/05/03/pthreads-with-mutex-and-semaphores/

print even and odd:

https://freethreads.wordpress.com/2012/05/05/two-indepedent-threads-to-print-even-and-odd-numbers-in-c/

reader and writer problem(using binary semaphore)

 pgm1:

/*
Name        :   Readers writers problem
Author      :   jishnu7
Web         :   http://thecodecracker.com
Date        :   26-Sep-2010
Description :   C Program to solve Dining philosophers problem
License     :   GNU GPL License
*/
#include <stdio.h>
#include <semaphore.h>
#include <pthread.h>
#define NUM_READ 2
#define NUM_WRIT 2
sem_t mutex;
sem_t db;
int reader_count=0;
int reader_name[]={1,2};
int writer_name[]={1,2};
void *reader(void *i);
void *writer(void *i);
int main()
{
  int i,j;
  pthread_t readers[NUM_READ];
  pthread_t writers[NUM_WRIT];
  if((sem_init(&mutex,0,1))<0)
    perror("ERROR");
  if((sem_init(&db,0,1))<0)
    perror("ERROR");
  for(i=0;i<NUM_READ;i++)
    if((pthread_create(&readers[i],NULL,reader,&reader_name[i]))!=0)
      perror("ERROR");
  for(j=0;j<NUM_WRIT;j++)
    if((pthread_create(&writers[j],NULL,writer,&writer_name[j]))!=0)
      perror("ERROR");
  for(i=0;i<NUM_READ;i++)
    if((pthread_join(readers[i],NULL))!=0)
      perror("ERROR");
  for(j=0;j<NUM_WRIT;j++)
    if((pthread_join(writers[j],NULL))!=0)
      perror("ERROR");
  sem_destroy(&mutex);
  sem_destroy(&db);
}
void *reader(void *n)
{
  int i=*(int *)n;
  if((sem_wait(&mutex))<0)
    perror("ERROR");
  reader_count++;
  if(reader_count==1)
    if((sem_wait(&db))<0)
      perror("ERROR");
  if((sem_post(&mutex))<0)
    perror("ERROR");
  printf("reader %d is reading\n",i);
  sleep(1);
  if((sem_wait(&mutex))<0)
    perror("ERROR");
  reader_count=reader_count-1;
  if((sem_post(&mutex))<0)
    perror("ERROR");
  if(reader_count==0)
  {
    if((sem_post(&db))<0)
      perror("ERROR");
  }
}
void *writer(void *n)
{
  int j=*((int *)n);
  printf("writer %d is waiting\n",j);
  if((sem_wait(&db))<0)
    perror("ERROR");
  printf("writer %d is writing\n",j);
  if((sem_post(&db))<0)
    perror("ERROR");
}


pgm2:

#include<stdio.h>
#include<pthread.h>
#include<semaphore.h>

sem_t readCountAccess;
sem_t databaseAccess;
int readCount=0;

void *Reader(void *arg);
void *Writer(void *arg);

int main()
{
 int i=0,NumberofReaderThread=0,NumberofWriterThread;
 sem_init(&readCountAccess,0,1);
 sem_init(&databaseAccess,0,1);

 pthread_t Readers_thr[100],Writer_thr[100];
 printf("\nEnter number of Readers thread(MAX 10)");
 scanf("%d",&NumberofReaderThread);
 printf("\nEnter number of Writers thread(MAX 10)");
 scanf("%d",&NumberofWriterThread);

 for(i=0;i<NumberofReaderThread;i++)
 {
  pthread_create(&Readers_thr[i],NULL,Reader,(void *)i);
 }
 for(i=0;i<NumberofWriterThread;i++)
 {
  pthread_create(&Writer_thr[i],NULL,Writer,(void *)i);
 }
 for(i=0;i<NumberofWriterThread;i++)
 {
  pthread_join(Writer_thr[i],NULL);
 }

 for(i=0;i<NumberofReaderThread;i++)
 {
  pthread_join(Readers_thr[i],NULL);
 }
 sem_destroy(&databaseAccess);
 sem_destroy(&readCountAccess);
 return 0;
}

void * Writer(void *arg)
{

 sleep(1);
 int temp=(int)arg;
 printf("\nWriter %d is trying to enter into database for modifying the data",temp);
 sem_wait(&databaseAccess);
 printf("\nWriter %d is writting into the database",temp);
 printf("\nWriter %d is leaving the database");
 sem_post(&databaseAccess);
}

void *Reader(void *arg)
{
 sleep(1);
 int temp=(int)arg;
 printf("\nReader %d is trying to enter into the Database for reading the data",temp);
 sem_wait(&readCountAccess);
 readCount++;
 if(readCount==1)
 {
  sem_wait(&databaseAccess);
  printf("\nReader %d is reading the database",temp);
 }
 sem_post(&readCountAccess);
 sem_wait(&readCountAccess);
 readCount--;
 if(readCount==0)
 {
  printf("\nReader %d is leaving the database",temp); 
  sem_post(&databaseAccess);
 }
 sem_post(&readCountAccess);
}
 




reference:
http://www.codingdevil.com/2014/04/c-program-for-reader-writer-problem.html
http://thecodecracker.com/c-programming/readers-writers-problem/

No comments:

Post a Comment