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.