pthread_cond_wait for 2 threads
Asked Answered
M

2

9

I'm trying to implement pthread_cond_wait for 2 threads. My test code is trying to use two threads to preform the following scenario:

  • Thread B waits for condition
  • Thread A prints "Hello" five times
  • Thread A signals thread B
  • Thread A waits
  • Thread B prints "Goodbye"
  • Thread B signals thread A
  • Loop to start (x5)

So far the code prints "Hello" five times and then gets stuck. From examples I've looked at it seems I'm on the right track, "Lock mutex, wait, get signaled by other thread, unlock mutex, do stuff, loop"

Test Code:

//Import 
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

//global variables
pthread_cond_t      condA  = PTHREAD_COND_INITIALIZER;
pthread_cond_t      condB  = PTHREAD_COND_INITIALIZER;
pthread_mutex_t     mutex = PTHREAD_MUTEX_INITIALIZER;




void *threadA()
{
    int i = 0, rValue, loopNum;

    while(i<5)
    {
        //unlock mutex
        rValue = pthread_mutex_unlock(&mutex);

        //do stuff
        for(loopNum = 1; loopNum <= 5; loopNum++)
            printf("Hello %d\n", loopNum);

        //signal condition of thread b
        rValue = pthread_cond_signal(&condB);

        //lock mutex
        rValue = pthread_mutex_lock(&mutex);

        //wait for turn
        while( pthread_cond_wait(&condA, &mutex) != 0 )

        i++;
    }

}



void *threadB()
{
    int n = 0, rValue;

    while(n<5)
    {
        //lock mutex
        rValue = pthread_mutex_lock(&mutex);

        //wait for turn
        while( pthread_cond_wait(&condB, &mutex) != 0 )

        //unlock mutex
        rValue = pthread_mutex_unlock(&mutex);

        //do stuff
        printf("Goodbye");

        //signal condition a
        rValue = pthread_cond_signal(&condA);

        n++;        
    }
}




int main(int argc, char *argv[])
{
    //create our threads
    pthread_t a, b;

    pthread_create(&a, NULL, threadA, NULL);
    pthread_create(&b, NULL, threadB, NULL);

    pthread_join(a, NULL);
    pthread_join(b,NULL);
}

A pointer in the right direction would be greatly appreciated, thanks! (Code compiled on Linux using "gcc timeTest.c -o timeTest -lpthread")

Meagan answered 2/12, 2012 at 23:26 Comment(1)
No it is not required, I was mainly trying variations but as you said it would be ideal to only use oneMeagan
D
29

You have two problems. The first is that you aren't using while() loops correctly - for example, here:

//wait for turn
while( pthread_cond_wait(&condA, &mutex) != 0 )

i++;

The body of the while loop is the statement i++ - this will execute pthread_cond_wait() and i++ until the pthread_cond_wait() returns an error, so this is essentially an endless loop.

The second is that you can't use a pthreads condition variable on its own - it needs to be paired with some actual shared state (at its simplest, this shared state might just be a flag variable protected by a mutex). The pthread_cond_wait() function is used to wait for the shared state to reach a certain value, and the pthread_cond_signal() function is used when a thread has altered the shared state. Reworking your example to use such a variable:

//global variables
/* STATE_A = THREAD A runs next, STATE_B = THREAD B runs next */
enum { STATE_A, STATE_B } state = STATE_A;
pthread_cond_t      condA  = PTHREAD_COND_INITIALIZER;
pthread_cond_t      condB  = PTHREAD_COND_INITIALIZER;
pthread_mutex_t     mutex = PTHREAD_MUTEX_INITIALIZER;

void *threadA()
{
    int i = 0, rValue, loopNum;

    while(i<5)
    {
        /* Wait for state A */
        pthread_mutex_lock(&mutex);
        while (state != STATE_A)
            pthread_cond_wait(&condA, &mutex);
        pthread_mutex_unlock(&mutex);

        //do stuff
        for(loopNum = 1; loopNum <= 5; loopNum++)
            printf("Hello %d\n", loopNum);

        /* Set state to B and wake up thread B */
        pthread_mutex_lock(&mutex);
        state = STATE_B;
        pthread_cond_signal(&condB);
        pthread_mutex_unlock(&mutex);

        i++;
    }

    return 0;
}

void *threadB()
{
    int n = 0, rValue;

    while(n<5)
    {
        /* Wait for state B */
        pthread_mutex_lock(&mutex);
        while (state != STATE_B)
            pthread_cond_wait(&condB, &mutex);
        pthread_mutex_unlock(&mutex);

        //do stuff
        printf("Goodbye\n");

        /* Set state to A and wake up thread A */
        pthread_mutex_lock(&mutex);
        state = STATE_A;
        pthread_cond_signal(&condA);
        pthread_mutex_unlock(&mutex);

        n++;
    }

    return 0;
}

Note that the use of two condition variables condA and condB is unnecessary here - the code would be just as correct if only one condition variable was used instead.

Disproportion answered 3/12, 2012 at 0:31 Comment(3)
Ah, embarrassing mistake with the while loop! Thanks for the input about using shared states, I understand the reasoning behind its usage. Regarding the usage of a single condition variable, I completely agree with what you're saying. Thanks a lot for the response, time to code the larger project, thanks again for the detailed response!Meagan
+1 nice description of the shared-state (often called the 'predicate') that cvar-mtx pairs are designed to manage.Jordonjorey
This is a great description of using pthread_cond_wait. Not only that but it is also a great code fragment to experiment with thread cond/mutex on your target. Thanks!Hemelytron
L
0

The code actually works almost fine on my machine when you add curly braces to the while loop.

Adding to what caf said, you'll enter an infinite loop when threadB is started after threadA has already sent the condB signal hence why you need to use a shared state in your while loop.

You can introduce artifical delay using usleep(1) on line 47 and see for yourself.

Loftis answered 25/3, 2017 at 20:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.