LinuxQuestions.org
Did you know LQ has a Linux Hardware Compatibility List?
Go Back   LinuxQuestions.org > Blogs > TheIndependentAquarius
User Name
Password

Notices

Rate this Entry

Why and how to use condition variables? - Pthreads

Posted 02-06-2013 at 01:40 AM by TheIndependentAquarius
Updated 02-06-2013 at 05:55 AM by TheIndependentAquarius

  • Q: What is a condition variable?
    A: A condition variable is a variable of type `pthread_cond_t`. It is
    used to suspend the thread execution until some condition is true.

  • Q: Is a condition variable dependent on a mutex lock to work properly? Why?
    A: Explaining with an example:

    Assumptions:
    1. There is a variable `x` whose current value is 0.
    2. There is a thread `threadA` whose task is to start some job once the
      variable `x` reaches the value 1000.
    3. There is a thread `threadB` whose task is to keep on incrementing the
      variable `x`'s value until it reaches 1000, and then send a signal
      for the threads waiting for this variable's value to turn 1000.

    Possible problematic situation:
    1. Scheduler schedules the thread `threadA`.
    2. Now, the thread `threadA` checks the value of variable `x` and finds
      that it hasn't yet reached 10. The thread `A` is now supposed to wait
      (by calling a particular pthread function) till that variable's value
      reaches 10.
    3. Scheduler schedules the thread `threadB`.
    4. Thread `threadB` increments the variable `x` changing its value to 10,
      and then calls the pthread function responsible for signaling the end
      of waiting period for thread `threadA`.
    5. Scheduler schedules the thread `threadA`.
    6. Thread `threadA` (unaware of the thread `threadB`'s deeds) now starts
      waiting for the variable `x` to change its value to 10. The signals of
      pthread do _not persist_. The thread `threadA` will now wait forever for
      the signal that has already come and gone.

    Solution to the above situation:
    We need to write a condition such that the thread `threadA` acquires the
    mutex lock _before_ checking the value of the variable `x` and
    releases it after waiting or deciding not to wait on the condition
    variable.

    Because the thread `threadA` locks the Mutex _before_ performing the critical
    activities (checking the variable's value and then waiting/not-waiting
    on the condition variable), the `thread B` simply can't change the
    variable's value and or send a signal till the `thread A` unlocks the mutex it
    held.

  • This code demonstrates the solution for the above problem attained by using
    the mutex lock
    .
Code:
#include <stdlib.h>
#include <pthread.h>
#include <iostream>


/* Mutex variable initialization. */
pthread_mutex_t mutexLock = PTHREAD_MUTEX_INITIALIZER;


/* Condition variable initialization. */
pthread_cond_t  cond       = PTHREAD_COND_INITIALIZER;


/* The functions to be executed by `ThreadA` and `ThreadB`. */
void* functionOfThreadA (void * ptr);
void* functionOfThreadB (void * ptr);


/* 
 * Initialization of the variable on whose value depends the
 * `ThreadA`'s further execution.
 */

int             x          = 0;

int main ()
{

    /* pthread_t: Used to uniquely identify a thread. */
    pthread_t threadA;
    pthread_t threadB;
    
    char      *messageA = (char *) "Thread A";
    char      *messageB = (char *) "Thread B";
    int       returnValueA;
    int       returnValueB;


    /* 
     * Arg 1: pthread_t *: Pointer of type `pthread_t` identifying
     * the unique name of the thread.
     * 
     * Arg 2: const pthread_attr_t *: Attributes of the thread.
     * Specifying NULL means setting default attributes.
     * 
     * Arg 3: void *(*start_routine) (void *): Function pointer
     * corresponding to the function to be executed by the
     * thread.
     * 
     * Arg 4: void* : Argument of the function to be executed by
     * the thread.
     */
    returnValueA = pthread_create (&threadA,
                        NULL,
                        functionOfThreadA,
                        (void*) messageA);
    
    returnValueB = pthread_create (&threadB,
                        NULL,
                        functionOfThreadB,
                        (void*) messageB);


    /*
     * We want the creater thread to wait till all the spawned
     * threads complete their tasks.
     * 
     * Arg 1: pthread_t: Thread ID of type `pthread_t` identifying
     * the unique name of the thread.
     * 
     * Arg 2: void**: If not NULL will contain the return value of
     * the function `pthread_join`.
     */   
    pthread_join (threadA, NULL);
    pthread_join (threadB, NULL);
}

void * functionOfThreadA (void * ptr)
{
    char * message;
    message = (char *) ptr;
    std :: cout << message << " has been scheduled now.\n";


    /*
     * The `threadA` will continiously (unless the scheduler
     * deschedules it) keep an eye on the critical section.
     */
    while (1)
    {

        /* 
         * `threadA` can lock the mutex only if `threadB`
         * hasn't locked it yet. This means that `threadB` can
         * increment the variable `x` and/or send the signal
         * only after `threadA` has started waiting or has
         * checked the value of the variable `x` and found it
         * as desired.
         * 
         * Arg 1: pthread_mutex_t *: Mutex variable to be
         * locked.
         */
        pthread_mutex_lock (&mutexLock);


        /* Is the condition satisfied? */
        if (x == 1000)
        {
            std :: cout << "\nA: x is now 1000. Waiting
period over."<< "\n";
            return 0;
        }

        /* If not, then the `threadA` will start `waiting`. */
        else
        {
            std :: cout << "\nNow, thread A will wait for
value of `x` to reach 1000" << "\n";

            /*
             * Arg 1: pthread_cond_t *: The condition
             * variable shared within the concerned
             * threads.
             * 
             * Arg 2: pthread_mutex_t *: The mutex
             * variable shared within the concerned
             * threads.
             */
            pthread_cond_wait (&cond, &mutexLock);
        }
        

        /* 
         * After the `threadA` starts waiting or decides not
         * to wait, the mutex will be unlocked.
         * 
         * Arg 1: pthread_mutex_t *: The mutex variable to be
         * unlocked.
         */
        pthread_mutex_unlock (&mutexLock);
    }
}

void * functionOfThreadB (void * ptr)
{
    char * message;
    message = (char *) ptr;
    std :: cout << message << " has been scheduled now.\n";


    /* 
     * The `threadB` will continiously (unless the scheduler
     * deschedules it) keep an eye on the critical section.
     */
    while (1)
    {

        /* 
         * `threadB` can lock the mutex only if `threadA`
         * hasn't locked it yet. This means that `threadB` can
         * increment the variable `x` and/or send the signal
         * only when `threadA` is NOT in its critical section.
         * 
         * This way either `threadA` will receive the signal
         * (if it is already waiting) or when it  enters its
         * critical section it'll simply find the variable
         * incremented to the desired value (thus will not
         * need to wait).
         */
        pthread_mutex_lock (&mutexLock);
        
        x = x + 1;

        /* Is the condition satisfied? */
        if (x == 1000)
        {
            std :: cout << "\nB: Signaled. Value of x: "
<< x << "\n";

            /* 
             * If yes, then send the signal to the waiting
             * thread(s).
             * 
             * Arg 1: pthread_cond_signal *: Condition
             * variable w.r.t to whom the signal is
             * supposed to be sent.
             */
            pthread_cond_signal (&cond);
            

            /* Unlock the mutex and return. */
            pthread_mutex_unlock (&mutexLock);
            return 0;
        }

        /* If not, then do nothing other than... */
        else
        {
             std :: cout << "\nB: Not signaled yet. Value
of x: " << x << "\n";
        }

        /* 
         * simply unlock the mutex and get out of the
         * critical section. 
         */
        pthread_mutex_unlock (&mutexLock);
    }
}
Output:

Code:
anisha@linux-trra:~> g++ conditionVariablesDemo.cpp -pthread
anisha@linux-trra:~> ./a.out 
Thread B has been scheduled now.

B: Not signaled yet. Value of x: 1

B: Not signaled yet. Value of x: 2

B: Not signaled yet. Value of x: 3

B: Not signaled yet. Value of x: 4

B: Not signaled yet. Value of x: 5

B: Not signaled yet. Value of x: 6

B: Not signaled yet. Value of x: 7

B: Not signaled yet. Value of x: 8

B: Not signaled yet. Value of x: 9

B: Not signaled yet. Value of x: 10

B: Not signaled yet. Value of x: 11
.
.
.
.
B: Not signaled yet. Value of x: 384

B: Not signaled yet. Value of x: 385

B: Not signaled yet. Value of x: 386

B: Not signaled yet. Value of x: 387

B: Not signaled yet. Value of x: 388

B: Not signaled yet. Value of x: 389

Thread A has been scheduled now.

Now, thread A will wait for value of `x` to reach 1000

B: Not signaled yet. Value of x: 390

B: Not signaled yet. Value of x: 391

B: Not signaled yet. Value of x: 392

B: Not signaled yet. Value of x: 393

B: Not signaled yet. Value of x: 394

.
.
.
.

B: Not signaled yet. Value of x: 986

B: Not signaled yet. Value of x: 987

B: Not signaled yet. Value of x: 988

B: Not signaled yet. Value of x: 989

B: Not signaled yet. Value of x: 990

B: Not signaled yet. Value of x: 991

B: Not signaled yet. Value of x: 992

B: Not signaled yet. Value of x: 993

B: Not signaled yet. Value of x: 994

B: Not signaled yet. Value of x: 995

B: Not signaled yet. Value of x: 996

B: Not signaled yet. Value of x: 997

B: Not signaled yet. Value of x: 998

B: Not signaled yet. Value of x: 999

B: Signaled. Value of x: 1000

A: x is now 1000. Waiting period over.
anisha@linux-trra:~>
References:
Posted in PThreads
Views 830 Comments 0
« Prev     Main     Next »
Total Comments 0

Comments

 

  



All times are GMT -5. The time now is 08:18 AM.

Main Menu
Advertisement

My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
identi.ca: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration