生产者消费者同步使用两个线程提供不正常的串行输出

Producer consumer sync using two threads giving unsual serial output

我正在使用两个线程解决生产者/消费者问题。我想我做错了什么,因为它似乎每次都提供相同的串行输出。有什么问题吗?

#include <stdio.h>
#include <semaphore.h>
#include <pthread.h>
#include <unistd.h>
#include <stdbool.h>

sem_t empty, full, mutex;

int bound[5];
int a,b;

void *producer(void *arg) {
    for (int i = 0; i < 15; i++) {
        sem_wait(&empty);
        sem_wait(&mutex);

        printf("%d is produced\n", i);
        sleep(1);

        bound[(++a) % 5] = i;

        sem_post(&mutex);
        sem_post(&full);
    }
}

void *consumer(void *arg) {
    int consumed;

    for (int i = 0; i < 15; i++) {
        sem_wait(&full);
        sem_wait(&mutex);

        consumed = bound[(++b) % 5];

        printf("%d is consumed\n",consumed);
        sleep(1);

        sem_post(&mutex);
        sem_post(&empty);
    }
}

void main() {
    pthread_t prod, con;

    sem_init(&mutex, 0, 1); 
    sem_init(&full, 0, 0);  
    sem_init(&empty, 0, 5);

    pthread_create(&prod,NULL,producer,NULL);
    pthread_create(&con,NULL,consumer,NULL);

    pthread_join(prod,NULL);
    pthread_join(con,NULL);
}

输出就像producer先消费5次,然后consumer全部消费,然后producer再produce,重复:

0 is produced
1 is produced
2 is produced
3 is produced
4 is produced
0 is consumed
1 is consumed
2 is consumed
3 is consumed
4 is consumed
5 is produced

等等。

仅仅因为一个线程发布了一个信号量,另一个线程正在等待,不能保证另一个线程立即到达 运行。如果第一个线程非常快,它可能会在另一个线程到达 运行 之前重新获得信号量。这可能是您看到 5 个生产和 5 个消费交替系列的原因。

尝试将 buf 变大(例如 1000)并在生产者和消费者中执行 1500 次循环(同时更改 sem_init)。然后你可能会看到另一种模式。

顺便说一句:删除两个睡眠调用。它们是在您按住 mutex 时完成的,因此其他线程无论如何都无法 运行。

一些线程正在等待信号量这一事实并不意味着一旦信号量的值变为非零它就一定会解除阻塞,即使它是当时唯一等待的线程。可能发生的事情之一是其他一些尚未等待的线程突然进入并首先获取它。

在你的代码中,例如,生产者和消费者都post到mutex信号量然后循环并尝试再次获取它。除非被 other 信号量阻塞,否则一个线程完全有可能释放 mutex 然后重新获取它,而另一个线程不会在两者之间获取它。事实上,您的消费者和生产者似乎都以五个项目为一组进行操作——对应于 emptyfull 信号量的计数——表明这实际上是正在发生的事情。

如果您将 sleep 调用移到线程函数的关键区域之外,我希望您能获得完美的交替。你应该这样做,因为否则每个线程只有一个很小的 ​​window 可以在其中接管另一个线程。

此外,虽然您可能会发现完美交替并不那么令人惊讶,但当您实现了一个长于一个项目的生产队列时,它也有些奇怪。我的意思是,如果队列中只有一项,为什么还要排队呢?

为了看到所用队列容量的更多变化,您应该改变生产者和消费者的时间。您可能希望使用比 sleep() 提供的更高分辨率的计时器来实现它,否则您的运行将花费不必要的时间。