如何让线程等待变量更改?

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

我希望函数 foo() 等待数组更新。结构 data 包含 foo() 方法的参数。我将 foo() 附加到一个线程,并且在 foo() 函数中有一个 while 循环,用于检查 updated 何时为 1(这意味着 main() 函数已更改数组。它应该在发生这种情况时打印数组。

这不会打印任何东西;该程序永远停滞不前。我不太熟悉线程,所以我不知道这是否是正确的方法。

#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);
}

编辑:

进行了答案中建议的更改后,这就是我现在的样子。它正在打印数组,但我希望它在数组更改时检测到(即每当 updated 设置为 true 时)。目前 foo 就像一个普通的非线程函数。

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

int array[5] = {0,0,0,0,0};

pthread_cond_t cv;
pthread_mutex_t mp;
int updated = 0;

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(void) {
    pthread_mutex_lock(&mp);
    int i;
    for (i = 0; i < 5; i++) {
        array[i] += 2;
    }
    updated = 1;
    pthread_cond_signal(&cv);
    pthread_mutex_unlock(&mp);
}

int main(void) {
    pthread_t thread;
    void *status;
    
    pthread_cond_init(&cv, NULL);
    pthread_cond_init(&cv2, NULL);
    pthread_mutex_init(&mp, NULL);
    pthread_mutex_init(&mp2, NULL);
    
    pthread_create(&thread, NULL, foo, &array);
    
    // """"""""""""
    // Change the array

    changeArray();

    // Wait for the second thread to finish printing

    pthread_mutex_lock(&mp2);
    while (!donePrinting)
        pthread_cond_wait(&cv2, &mp2);
    changeArray();
    donePrinting = 0;
    pthread_mutex_unlock(&mp2);
return 0;
    // Change the array again
    

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

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

你需要在这个程序的某个点调用pthread_cond_signal

首先是台词

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

错了。对同一个条件变量 cv 调用两次 pthread_cond_init 是没有意义的。另外,如果你使用cattr,你应该先初始化它,然后再传递给pthread_cond_init。如果你想使用默认条件变量属性,那么你应该简单地传递 NULL 而不是 &cattr。你可能想做后者。在这种情况下,您应该简单地删除第二行并删除 cattr.

的声明

另外,你必须在使用前初始化互斥量mp,使用pthread_mutex_init

int rc = pthread_join(thread, &status);

将导致主线程等待第二个线程完成。然而,这永远不会发生,因为行

temp.updated = 1;

第二个线程正在等待,稍后在主线程中发生。因此,你应该移动

int rc = pthread_join(thread, &status);

行后:

temp.updated = 1;

此外,您应该在

行之后添加对 pthread_cond_signal 的调用
temp.updated = 1;

但在行之前

int rc = pthread_join(thread, &status);

以便第二个线程得到通知,它应该再次检查变量。

此外,在第二个线程中,行

while (d->updated == 0) {

是错误的,因为执行该行时您没有持有互斥锁。这会导致未定义的行为(由于共享变量上的竞争条件)。

因此,如果要保持d->updated == 0循环条件,则必须移动

pthread_mutex_lock(&mp);

行前:

while (d->updated == 0) {

这意味着你将有两次重组你的循环,因为pthread_mutex_unlock的位置也必须在正确的地方。

但是,我怀疑您保留 d->updated == 0 循环条件是否合适。如果它应该是一个无限循环,那么你可以写 for (;;)while ( 1 ) 来代替,这样你就不会遇到在循环条件下访问共享变量的问题。


关于您更新的代码,我相信这是该程序的工作版本,它可以完成您想要的一切:

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

int array[5] = {0};

pthread_cond_t cv;
pthread_mutex_t mp;
int updated = 0;
int terminate = 0;

pthread_cond_t cv2;
pthread_mutex_t mp2;
int donePrinting = 0;

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

    int* arr = a;
    
    while ( 1 ) {
        while ( updated == 0 && terminate == 0 )
            pthread_cond_wait(&cv, &mp);

        if ( terminate )
            break;

        printf("Updated: ");
        for (int i = 0; i < 5; i++) {
            printf("%d ", arr[i]);
            fflush( stdout );
        }
        printf("\n");
        fflush( stdout );
        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(void) {
    pthread_mutex_lock(&mp);
    int i;
    for (i = 0; i < 5; i++) {
        array[i] += 2;
    }
    updated = 1;
    pthread_cond_signal(&cv);
    pthread_mutex_unlock(&mp);
}

int main(void) {
    pthread_t thread;
    void *status;
    
    pthread_cond_init(&cv, NULL);
    pthread_cond_init(&cv2, NULL);
    pthread_mutex_init(&mp, NULL);
    pthread_mutex_init(&mp2, NULL);
    
    pthread_create(&thread, NULL, foo, (void*)&array);
    
    // """"""""""""
    // Change the array

    changeArray();

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

    //reset donePrinting
    pthread_mutex_lock(&mp2);
    donePrinting = 0;
    pthread_mutex_unlock(&mp2);

    // Change the array again
    changeArray();

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

    //reset doneprinting
    pthread_mutex_lock(&mp2);
    donePrinting = 0;
    pthread_mutex_unlock(&mp2);

    // Change the array a third time
    changeArray();

    // """"""""""""""""

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

    //send terminate request
    pthread_mutex_lock(&mp);
    terminate = 1;
    pthread_cond_signal(&cv);
    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);
}

我更改了程序,当处理完成并且没有任何东西要发送时,主线程向第二个线程发送终止请求。