Search code examples
cpthreadslockingmutexcondition-variable

Condition variables in c


I just started learning about threads, mutexes and condition variables and I have this code:


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

static pthread_cond_t cond = PTHREAD_COND_INITIALIZER; 
static pthread_mutex_t mutex; 
static pthread_mutexattr_t attr;

volatile int x = 0;

void *thread_one_function (void *dummy) {
    printf("In func one.");
    while (true) {
        pthread_mutex_lock (&mutex);
        x = rand ();
        pthread_cond_signal (&cond);
        pthread_mutex_unlock (&mutex);
    }
}

void *thread_two_function (void *dummy) {
    printf("In func two.");
    pthread_mutex_lock (&mutex);
    while (x != 10) {
        pthread_cond_wait (&cond, &mutex);
    }
    printf ("%d\n", x);
    pthread_mutex_unlock (&mutex);
    printf ("%d\n", x);
}

int main (void){
    pthread_mutexattr_init (&attr);
    pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE);
    pthread_mutex_init (&mutex, &attr);

    pthread_t one, two;
    pthread_create(&one,NULL,thread_one_function,NULL);
    pthread_create(&two,NULL,thread_two_function,NULL);

    //pthread_join(one,NULL); //with this program will not end.
    //pthread_join(two,NULL);

    return 0;
}

I compile it as gcc prog.c -lpthread -o a.exe

And I am getting no output. Not even that my threads get into those two functions... What am I doing wrong ? My code is created as a combination from multiple documentations. Thanks for any help.


Solution

  • The most likely reason for you getting no output is that the main thread returns from the initial call to main() immediately after starting the threads. That terminates the program, including the newly-created threads.

    It does not help that neither do the threads' initial messages end with newlines nor do the threads flush the standard output after the printf calls. The standard output is line-buffered when connected to a terminal, so actual delivery of those messages to the terminal will be deferred in your example.

    The main thread should join the other two before it terminates. Your code comments indicate that you had other issues when you did that, but going without joining those threads is not a correct solution if in fact you want the additional threads to run to completion. The problem is not joining the threads, but rather that the threads are not terminating.

    There's a pretty clear reason why the thread_one_function thread does not terminate, which I am sure you will recognize if you look for it. But what about the thread_two_function thread? The reasons for that taking a long time to terminate are at least twofold:

    • the more obvious one is the one hinted in @dbush's comment on the question. Consider the range of the rand() function: how many calls do you think it might take before it returns the one, specific result that thread_two_function is looking for?

    • moreover, how often is thread_two_function even going to get a chance to check? Consider: thread_one_function runs a tight loop in which it acquires the mutex at the top and releases it at the bottom. Pthreads mutexes do not implement any kind of fairness policy, so when thread_one_function loops around and tries to reacquire the mutex immediately after releasing it, it has a very high probability of succeeding immediately, even though thread_two_function may be trying to acquire the mutex too.

    It would make more sense for your threads to take turns. That is, after generating a new number, thread_one_function would wait for thread_two_function to check it before generating another. There are several ways you could implement that. Some involve using the same condition variable in thread_one_function that you do in thread_two_function. (Details left as an exercise.) I would suggest also providing a means by which thread_two_function can tell thread_one_function that it doesn't need to generate any more numbers, and should instead terminate.

    Finally, do be aware that volatile has no particular place here. It is not useful or appropriate for synchronization, whereas the mutex alone is entirely sufficient for that. It's not exactly wrong to declare x volatile, but it's extraneous.