[Linux] Producer Consumer Model

What is the producer-consumer model?

  Producer-consumer is a well-known problem. Its description is: a group of producer processes are producing products and provide these products to consumer processes for consumption. In order to enable concurrent execution of producers and consumers, n buffers are set between the two The producer process puts the products it produces into a buffer pool; the consumer process can take products from a buffer. They must be in sync, i.e. consumers are not allowed to fetch products into an empty buffer, nor producers to put products into a buffer that is already full. When the buffer is empty, the consumer process needs to suspend and sleep until the producer process puts the product into the buffer, and the consumer can be woken up; on the contrary, if the buffer is full, the consumer process needs to suspend until the buffer is full. Only when the products in it are taken away by consumers can they be awakened.

  To give an example: We compare the buffer zone to a supermarket where we usually shop. The producers are the suppliers, and the consumers are us who go to the supermarket to buy things.
  - When there is nothing to sell on the shelves of the supermarket, we need to wait for the goods to be delivered by the supplier before we can buy them. The shelves of the supermarket are full of goods, and we can only continue to add goods until we take them away. This is Synchronization between producers and consumers;
  - We can only buy goods when the supermarket is ready, on the contrary, the supermarket can only put the goods after we have finished; this is a mutual exclusion between producers and consumers ( Although this example is not appropriate in real life, it does so in computers);
  - If there is a certain product in the supermarket, but many consumers may want it, this is the competition between consumers;
  - If there is only one city in a certain area, then there may be many producers who want to put their products in supermarkets, which is competition among producers.

So, to summarize the above:

  • Three relationships:
    • Consumers and Producers: Mutual Exclusion and Synchronization
    • Consumer and Consumer: Mutual Exclusion (Competition)
    • Producer and Producer: Mutual Exclusion (Competition)
  • Two roles: consumer and producer
  • A trading place: with buffers to store data

Based on linked list, implement producer-consumer using mutex and condition variable

We use threads to simulate consumers and producers in a Linux environment. The insertion operation of the linked list is used to simulate the production of producers, and the delete operation of the linked list is used to simulate the consumption of products by consumers.
For the use of mutexes and condition variables, you can refer to the previous blog .
The specific implementation code is as follows:

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

pthread_cond_t cond = PTHREAD_COND_INITIALIZER; //条件变量
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;//互斥量

typedef struct node//链表结点
{
    int data;
    struct node* next;
}node, *node_p, **node_pp;

node_p getnode(int data)//创建结点
{
    node_p ret = malloc(sizeof(node));
    if(ret != NULL)
    {
        ret->data = data;
        ret->next = NULL;
        return ret;
    }
    return NULL;
}

void initlist(node_pp pphead)//初始化链表——创建头结点
{
    if(pphead == NULL)
        exit(1);
    *pphead = malloc(sizeof(node));
    (*pphead)->next = NULL;
}

int isEmpty(node_p phead)//链表判空,空返回1
{
    return (phead->next == NULL)?1:0;
}

void push(node_p phead, int data)//链表的头插
{
    node_p tmp = getnode(data);
    if(tmp != NULL)
    {
        tmp->next = phead->next;
        phead->next = tmp;
    }
}

void print(node_p phead)//打印链表结点的数据
{
    phead = phead->next;
    while(phead)
    {
        printf("%d  ", phead->data);
        phead = phead->next;
    }
}

void erase(node_p phead, int *x)//删除链表的数据
{

    if(isEmpty(phead))
        return;
    node_p cur = phead->next;
    phead->next = cur->next;
    *x = cur->data;
    free(cur);
}

void destroy(node_p phead)//销毁链表
{
    while(!isEmpty(phead))
    {
        int data;
        erase(phead, &data);
    }
    free(phead);
}

void *producer(void* arg)//生产者线程执行的函数
{
    node_p phead = (node_p)arg;
    while(1)
    {
        int data = rand()%1000;
        pthread_mutex_lock(&mutex);
        push(phead, data);
        printf("producer done, %d\n", data);
        pthread_mutex_unlock(&mutex);
        pthread_cond_signal(&cond);
        sleep(1);
    }
}

void* consumer(void* arg)//消费者线程执行的函数
{
    node_p phead = (node_p)arg;
    int data = 0;
    while(1)
    {
        pthread_mutex_lock(&mutex);
        while(isEmpty(phead))
        {
            printf("no product, please wait...\n");
            pthread_cond_wait(&cond, &mutex);
        }
        erase(phead, &data);
        printf("consumer done, %d\n", data);
        pthread_mutex_unlock(&mutex);
        usleep(100000);
    }
}
int main()
{
    node_p phead;
    initlist(&phead);

    srand((unsigned long)time(NULL));
    pthread_t t1, t2;
    pthread_create(&t1, NULL, producer, (void*)phead);//生产者线程
    pthread_create(&t2, NULL, consumer, (void*)phead);//消费者线程

    pthread_join(t1, NULL);//线程等待
    pthread_join(t2, NULL);

    destroy(phead);//释放链表
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);
    return 0;
}

operation result:
write picture description here


Based on a circular queue, using POSIX semaphores to implement producer-consumer

Similarly, in the Linux environment, we use threads to simulate producers and consumers.
First, introduce POSIX semaphore, which has the same function as System V semaphore to achieve the purpose of accessing shared resources without conflict, but POSIX can be used for synchronization between threads.
1. Initialize the semaphore

#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);

parameter:

  • pshared: 0 means shared between threads, non-0 means shared between processes
  • value: the initial value of the semaphore

2. Destroy the semaphore

int sem_destroy(sem_t *sem);

3. Wait for the semaphore

等待信号量,会将信号量的值减1,相当于P操作
int sem_wait(sem_t *sem);

4. Release semaphore

发布信号量,表示资源使用完毕。将信号量值加1,相当于V操作
int sem_post(sem_t *sem);

circular queue
write picture description here

Specific implementation code:

#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<pthread.h>
#include<unistd.h>
#include<semaphore.h>
#include<time.h>

#define SIZE 10
sem_t sem_blank;
sem_t sem_data;

int ring[SIZE];

void* produce(void* arg)
{
    int i = 0;
    while(1)
    {
        sem_wait(&sem_blank);
        int data = rand()%1234;
        ring[i] = data;
        i++;
        i %= SIZE;
        printf("producer done...data:%d\n", data);
        sem_post(&sem_data);
        sleep(1);
    }
}

void* consume(void* arg)
{
    int i = 0;
    while(1)
    {
        sem_wait(&sem_data);
        int data = ring[i];
        i++;
        i %= SIZE;
        printf("consumemer done...data:%d\n", data);
        sem_post(&sem_blank);
        usleep(1000);
    }
}
int main()
{
    srand((unsigned long)time(NULL));

    pthread_t t1, t2;
    sem_init(&sem_blank, 0, SIZE);
    sem_init(&sem_data, 0, 0);

    pthread_create(&t1, NULL, produce, NULL);
    pthread_create(&t2, NULL, consume, NULL);

    pthread_join(t1, NULL);
    pthread_join(t2, NULL);

    sem_destroy(&sem_blank);
    sem_destroy(&sem_data);

    return 0;
}

operation result:
write picture description here

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325627037&siteId=291194637