在 C 中使用信号量的生产者-消费者

Producer-consumer using semaphore in C

存在生产者消费者问题是用semaphore写的。在下面的代码中,存在创建消费者时同步执行的问题。对于它的解决方案,在消费者的开关块中添加了睡眠语句。
请帮助我提供有效的同步解决方案。任何改进代码的建议都非常有帮助。

#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
#include<semaphore.h> /* sem_t */
#include<stdlib.h>

#define BUF_SIZE 2

struct buffer {
        int data;
};

struct buffer buf[BUF_SIZE];

sem_t fill, empty;

int value, i;

void *producer(void *data);
void *consumer(void *data);

int main(void)
{
    int sel, prod_data, cons_data;
    int k;

    if (sem_init(&empty, 0, BUF_SIZE)) {
        printf("Error: semaphore not initialize\n");
        return -1;
    }
    if (sem_init(&fill, 0, 0)) {
        printf("Error: semaphore not initialize\n");
        return -1;
    }

    while (1) {
        printf(".........................................................\n");
        printf("Selection\n");
        printf("Producer : 1 | Consumer : 2 | Display : 3 | Exit : 0 || ");
        scanf("%d",&sel);
        printf(".........................................................\n");

        switch (sel) {
        case 1:
            sem_getvalue(&empty, &value);
//          printf("Prod_e: %d\n", value);
            sem_getvalue(&fill, &value);
//          printf("Prod_f: %d\n", value);
            printf("\nProducer\n");
            pthread_t prod_t;
            printf("Enter data:");
            scanf("%d", &prod_data);
            if (pthread_create(&prod_t, NULL, producer, (void *) &prod_data)) {
                printf("Error: thread not created\n");
                return -1;
            }
            break;
        case 2:
            printf("\nConsumer\n");
            sem_getvalue(&empty, &value);
//          printf("Cons_e: %d\n", value);
            sem_getvalue(&fill, &value);
//          printf("Cons_f: %d\n", value);
            pthread_t con_t;
            if (pthread_create(&con_t, NULL, consumer, (void *) &cons_data)) {
                printf("Error: thread not created\n");
                return -1;
            }
            if (i == 0) {
                printf("Buffer empty\n");
                break;
            }
//              sleep(1); // if commented then synchronization issue
            printf("Consume data: %d\n", cons_data);
            break;
        case 3:
                if (i == 0) {
                    printf("Buffer empty\n");
                    break;
                }
                for (k = 0; k < i; k++)
                    printf("buf[%d]: %d\n", k, buf[k].data);
                break;
        case 0:
            sem_destroy(&fill);
            sem_destroy(&empty);
            exit (0);
            break;
        }
    }
    sem_destroy(&fill);
    sem_destroy(&empty);
    return 0;
}

void *producer(void *arg)
{
    int data = *(int *) arg;

    if (sem_wait(&empty)) { /* wait */
        printf("Error: sem wait fail\n");
        pthread_exit(NULL);
    }

    buf[i].data = data;
    i++;

    if (sem_post(&fill)) { /* post */
        printf("Error: sem wait fail\n");
        pthread_exit(NULL);
    }

    pthread_exit(NULL);
}

void *consumer(void *arg)
{

    if (sem_wait(&fill)) { /* wait */
        printf("Error: sem wait fail\n");
        pthread_exit(NULL);
    }

    i--;
    *(int *) arg = buf[i].data;

    if (sem_post(&empty)) { /* post */
        printf("Error: sem wait fail\n");
        pthread_exit(NULL);
    }
    pthread_exit(NULL);
}


没有睡眠语句的输出:

Selection
Producer : 1 | Consumer : 2 | Display : 3 | Exit : 0 || 1

Producer
Enter data: 11

Selection
Producer : 1 | Consumer : 2 | Display : 3 | Exit : 0 || 3

Display
buf[0] = 11

Selection
Producer : 1 | Consumer : 2 | Display : 3 | Exit : 0 || 2

Consumer
Consume data: 4196464

如果在消费者块中添加睡眠语句,则获得预期输出(在我们的例子中为 11)。
由于插入了睡眠语句,与 switch 的情况 2 语句相比,消费者线程有足够的时间来完成其执行并提供预期的输出。

问题在于您的主线程运行到它显示消费者线程应该显示的数据的程度"return"。但是主线程和消费者之间没有同步。

最简单的解决方案(仍然使用线程)是让主线程加入消费者线程——这就是它知道消费者已经完成其工作的方式。而不是调用睡眠,尝试:

pthread_join(con_t, NULL);

您实际上与 producer() 线程有类似的同步问题,只是它不明显,因为在获得更多输入之前,您不会对生产线程更新的数据做任何事情。因此竞争条件有足够的时间以产生您期望的结果的方式完成。

但是,请记住,您的程序实际上并没有利用并发性 - 它创建了线程,但并没有并行执行任何重要的工作。您也可以直接调用 producer()consumer()