Home > OS >  How do I make a thread wait for a variable to change?
How do I make a thread wait for a variable to change?

Time:03-09

I want the function foo() to wait for an array to be updated. The struct data contains the arguments to the foo() method. I attach foo() to a thread and I have a while loop in the foo() function that checks for when updated is 1 (which means the main() function changed the array. It's supposed to print the array when that happens.

This doesn't print anything; the program just stalls forever. I'm not too familiar with threading so I don't know if this is the right way to do this.

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

struct data {
    int array[5];
    int updated;
};

pthread_cond_t cv;
pthread_mutex_t mp;
pthread_condattr_t cattr;
int ret;

void* foo(void* da) {
    struct data* d = (struct data*)da;
    while (d->updated == 0) {
        pthread_mutex_lock(&mp);
        if (d->updated == 1) {
            pthread_cond_wait(&cv, &mp);
            printf("Updated: ");
            for (int i = 0; i < 5; i  ) {
                printf("%d ", d->array[i]);
            }
            printf("\n");
            d->updated = 0;
        }
        pthread_mutex_unlock(&mp);
    }
    pthread_exit(NULL);
}

int main(void) {
    pthread_t thread;
    pthread_attr_t attr;
    void *status;
    struct data temp;
    temp.updated = 0;

    ret = pthread_cond_init(&cv, NULL);
    ret = pthread_cond_init(&cv, &cattr);

    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
    pthread_create(&thread, NULL, foo, (void *) &temp);
    int rc = pthread_join(thread, &status);
    if (rc) {
        printf("ERROR; return code from pthread_join() is %d\n", rc);
        exit(-1);
    }
    
    pthread_mutex_lock(&mp);
    for (i = 0; i < 5; i  ) {
        temp.array[i] *= 2;
    }
    temp.updated = 1;
    pthread_mutex_unlock(&mp);
    pthread_exit(NULL);
}

Edit:

After making the changes recommended in the answers, this is what I have now. It's printing the array, but I want it to detect whenever the array is changed (i.e whenever updated is set to true). At the moment foo is just acting like a regular non-threaded function.

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

int array[5] = {};

pthread_cond_t cv;
pthread_mutex_t mp;
int updated;

pthread_cond_t cv2;
pthread_mutex_t mp2;
int donePrinting = 0;

void* foo(void* a) {
    pthread_mutex_lock(&mp);

    int* arr = *(int**)a;
    
    while (1) {
        while (updated == 0)
            pthread_cond_wait(&cv, &mp);
        printf("Updated: ");
        for (int i = 0; i < 5; i  ) {
            printf("%d ", arr[i]);
        }
        printf("\n");
        updated = 0;

        pthread_mutex_lock(&mp2);
        donePrinting = 1;
        pthread_cond_signal(&cv2); // Signal to main that we are done printing
        pthread_mutex_unlock(&mp2);
    }
    pthread_mutex_unlock(&mp);
    
    pthread_exit(NULL);
}

void changeArray() {
    for (int i = 0; i < 5; i  ) {
        array[i]  = 2;
    }
    updated = 1;
    pthread_cond_signal(&cv);
}

int main(void) {
    pthread_t thread;
    pthread_attr_t attr;
    void *status;
    
    pthread_cond_init(&cv, NULL);
    pthread_attr_init(&attr);
    pthread_mutex_init(&mp, NULL);
    pthread_mutex_init(&mp2, NULL);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
    pthread_create(&thread, NULL, foo, &array);
    
    // """"""""""""

    // Change the array
    pthread_mutex_lock(&mp);
    changeArray();
    pthread_mutex_unlock(&mp);

    // Wait for the second thread to finish printing
    pthread_mutex_lock(&mp2);
    while (!donePrinting)
        pthread_cond_wait(&cv2, &mp2);
    pthread_mutex_unlock(&mp2);

    // Change the array again
    pthread_mutex_lock(&mp);
    changeArray();
    pthread_mutex_unlock(&mp);

    // Wait for the second thread to finish printing
    pthread_mutex_lock(&mp2);
    while (!donePrinting)
        pthread_cond_wait(&cv2, &mp2);
    pthread_mutex_unlock(&mp2);

    // Change the array a third time
    pthread_mutex_lock(&mp);
    changeArray();
    pthread_mutex_unlock(&mp);

    // """"""""""""""""
    
    int rc = pthread_join(thread, &status);
    if (rc) {
        printf("ERROR; return code from pthread_join() is %d\n", rc);
        exit(-1);
    }
    
    pthread_exit(NULL);
}

CodePudding user response:

First of all, the lines

ret = pthread_cond_init(&cv, NULL);
ret = pthread_cond_init(&cv, &cattr);

are wrong. It does not make sense to call pthread_cond_init twice on the same condition variable cv. Also, if you use cattr, you should initialize it first before passing it to pthread_cond_init. If you want to use default condition variable attributes instead, then you should simply pass NULL instead of &cattr. You probably want to do the latter. In that case, you should simply delete the second line and also delete the declaration of cattr.

Also, you must initialize the mutex mp before using it, using pthread_mutex_init.

The line

int rc = pthread_join(thread, &status);

will cause the main thread to wait until the second thread has finished. However, this will never happen, because the line

temp.updated = 1;

which the second thread is waiting for, occurs later in the main thread. Therefore, you should move

int rc = pthread_join(thread, &status);

after the line:

temp.updated = 1;

Also, you should add a call to pthread_cond_signal after the line

temp.updated = 1;

but before the line

int rc = pthread_join(thread, &status);

so that the second thread gets gets notified that it should check the variable again.

Also, in the second thread, the line

while (d->updated == 0) {

is wrong, because you are not holding the mutex when you execute that line. This causes undefined behavior (due to a race condition on the shared variable).

Therefore, if you want to keep the d->updated == 0 loop condition, then you must move the line

pthread_mutex_lock(&mp);

before the line:

while (d->updated == 0) {

This means that you will have two restructure your loops, because the position of pthread_mutex_unlock must also be in the right place.

However, I doubt that it is appropriate that you keep the d->updated == 0 loop condition. If that it supposed to be an infinite loop, then you can write for (;;) or while ( 1 ) instead, so that you don't have the problem of accessing a shared variable in the loop condition.

CodePudding user response:

You need to call pthread_cond_signal at a certain point in this program.

  • Related