您好,我正在创建一个简单的计数器,使用生产者-消费者方法来通知一个线程何时完成,以便另一个线程可以运行,这允许它们交替.当我运行代码时,计数按预期从0到10打印了10次.但在计数的每一次打印之间,我得到了十几个锁和解锁的 fingerprint ,以及信号,这让我认为我的代码设置错误.

消费者和生产者端的每个迭代不应该只有一个锁定、解锁和信号吗?输出示例如下:

myCount: 6 -> 7
Producer: signaling myCond1
Producer: waiting on myCond2
CONSUMER: myMutex locked
CONSUMER: signaling myCond2
CONSUMER: myMutex unlocked
Producer: myMutex unlocked
Producer: myMutex locked
CONSUMER: myMutex locked
CONSUMER: signaling myCond2
CONSUMER: myMutex unlocked
myCount: 7 -> 8
Producer: signaling myCond1
Producer: waiting on myCond2
CONSUMER: myMutex locked
CONSUMER: signaling myCond2
CONSUMER: myMutex unlocked
Producer: myMutex unlocked
Producer: myMutex locked
CONSUMER: myMutex locked
CONSUMER: signaling myCond2
CONSUMER: myMutex unlocked
myCount: 8 -> 9
Producer: signaling myCond1
Producer: waiting on myCond2
CONSUMER: myMutex locked
CONSUMER: signaling myCond2
CONSUMER: myMutex unlocked
Producer: myMutex unlocked
Producer: myMutex locked
CONSUMER: myMutex locked
CONSUMER: signaling myCond2
CONSUMER: myMutex unlocked
myCount: 9 -> 10
Producer: signaling myCond1
Producer: waiting on myCond2
CONSUMER: myMutex locked
CONSUMER: signaling myCond2
Producer: myMutex unlocked
Producer: myMutex locked
PROGRAM END

以下是代码:

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

// Global variables
pthread_mutex_t myMutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t myCond1 = PTHREAD_COND_INITIALIZER;
pthread_cond_t myCond2 = PTHREAD_COND_INITIALIZER;
int myCount = 0;

// Function prototypes
void* producerThread(void* arg);
void* consumerThread(void* arg);

int main() {
    // Create consumer thread
    pthread_t consumer;
    printf("PROGRAM START\n");

    if (pthread_create(&consumer, NULL, consumerThread, NULL) != 0) {
        perror("Error creating consumer thread");
        exit(EXIT_FAILURE);
    }

    // Run the producer thread
    producerThread(NULL);

    // Wait for the consumer thread to finish
    pthread_join(consumer, NULL);

    printf("PROGRAM END\n");

    return 0;
}

void* producerThread(void* arg) {
    while (1) {
        printf("Producer: myMutex locked\n");
        pthread_mutex_lock(&myMutex);
        
        if (myCount >= 10) {
            pthread_mutex_unlock(&myMutex);
            break;  // Terminate the program when myCount reaches 10
        }

        myCount++;
        printf("myCount: %d -> %d\n", myCount - 1, myCount);

        // Signal the consumer that a new value is available
        printf("Producer: signaling myCond1\n");
        pthread_cond_signal(&myCond1);

        printf("Producer: waiting on myCond2\n");
        pthread_cond_wait(&myCond2, &myMutex);

        pthread_mutex_unlock(&myMutex);
        printf("Producer: myMutex unlocked\n");
    }

    return NULL;
}

void* consumerThread(void* arg) {
    printf("CONSUMER THREAD CREATED\n");

    while (1) {
        pthread_mutex_lock(&myMutex);
        printf("CONSUMER: myMutex locked\n");

        // Wait for the producer to signal that a new value is available
        while (myCount == 0) {
            printf("CONSUMER: waiting on myCond1\n");
            pthread_cond_wait(&myCond1, &myMutex);
        }

        // Signal the producer that the value has been consumed
        printf("CONSUMER: signaling myCond2\n");
        pthread_cond_signal(&myCond2);

        if (myCount >= 10) {
            pthread_mutex_unlock(&myMutex);
            break;  // Terminate the program when myCount reaches 0
        }
        
        pthread_mutex_unlock(&myMutex);
        printf("CONSUMER: myMutex unlocked\n");
    }

    return NULL;
}

推荐答案

你的消费者只会输入一个条件--当myCount等于零时等待.一旦遇到非零值,消费者就会变得贪婪,并会不断地旋转以获取互斥体和发送信号.

您可能希望保留使用者处理的最后一个值的本地副本,并输入您的条件-当该值自上次迭代以来没有更改时等待:

void* consumerThread(void* arg) {
    printf("CONSUMER THREAD CREATED\n");

    int lastCount = 0;
    while (1) {
        pthread_mutex_lock(&myMutex);
        printf("CONSUMER: myMutex locked\n");

        // Wait for the producer to signal that a new value is available
        while (myCount == lastCount) {
            printf("CONSUMER: waiting on myCond1\n");
            pthread_cond_wait(&myCond1, &myMutex);
        }
        lastCount++;

        // Signal the producer that the value has been consumed
        printf("CONSUMER: signaling myCond2\n");
        pthread_cond_signal(&myCond2);

        if (lastCount>= 10) {
            pthread_mutex_unlock(&myMutex);
            break;
        }
        
        printf("CONSUMER: unlocking myMutex\n");
        pthread_mutex_unlock(&myMutex);
    }

    return NULL;
}

另外,在两个函数中都有一个争用条件,在这两个函数中打印大约unlocking个互斥量的消息.因为这是在解锁之后进行的,所以另一个线程甚至可以在代码命中之前获取互斥锁并打印消息.

我建议您在释放互斥锁之前将printf移至发生,以避免额外的混淆.请注意,我已经在我的代码示例中做到了这一点.您应该在生产者函数中进行类似的更改.

还要注意的是,制片人很容易受到虚假唤醒的影响.您假设pthread_cond_wait只有在发出条件变量信号时才会被唤醒.事实并非如此.阅读此文可了解更多信息:Why does pthread_cond_wait have spurious wakeups?

为了解决这个问题,并在您的生产者和消费者之间创建真正的乒乓依赖关系,您可以将这lastCount个变量设为全局变量,并以与消费者相同的方式在生产者中运行一个内部While循环:

    while (myCount != lastCount) {
        printf("Producer: waiting on myCond2\n");
        pthread_cond_wait(&myCond1, &myMutex);
    }

C++相关问答推荐

如何将一个integer与一个数组进行比较?

数组元素的编号索引

如何将已分配的数组(运行时已知的大小)放入 struct 中?

使用GOTO从多个嵌套循环C继续

为什么GCC C23中的关键字FALSE不是整数常量表达式?

以下声明和定义之间的区别

如何将常量char*复制到char数组

为什么我从CSV文件中进行排序和搜索的代码没有显示数据的所有结果?

C中的FREE函数正在触发断点

如何使用C++在控制台中以彩色打印被阻止的客户端

GCC奇怪的行为,有fork 和印花,有换行符和不换行符

如何使用FSeek和文件流指针在C中查找文件的前一个元素和前一个减go 一个元素

用C++构建和使用DLL的困惑

为什么编译器不能简单地将数据从EDI转移到EAX?

条件跳转或移动取决于未初始化值(S)/未初始化值由堆分配创建(Realloc)

不带Malloc的链表

为什么会导致分段故障?(C语言中的一个程序,统计文件中某个单词的出现次数)

使用 strtok 多次分割一个字符串会导致意外行为

GDB 跳过动态加载器代码

初始化动态分配的布尔二维数组的最佳方法是什么?