Producer/consumer issues

In modular programming, high cohesion and low coupling are usually the criteria we need to achieve, which is believed to be a problem for producers and consumers.

In the study of threads, I have seen this example, so I will learn it here.

The problem of producer and consumer is a classical thread synchronization problem. In real life, it is easy to encounter, also known as the limited buffer problem. The main role of producer is to generate a certain amount of data into the buffer, and then repeat the process. At the same time, consumers are consuming the data in the buffer. The key to this problem is to ensure that producers do not add data when the buffer is full and consumers do not consume data when the buffer is empty. To solve this problem, the producer must sleep when the buffer is full (or abandon the data altogether) until the next time the consumer consumes the data in the buffer, the producer can be awakened and start adding data to the buffer. Similarly, consumers can sleep in the buffer space until the producer adds data to the buffer before waking up the consumer.

The above is Baidu Encyclopedia's explanation for this problem, and my own understanding is that one thread writes to the buffer, the other reads from the buffer. When the buffer is full, I no longer write. At this time, the writing thread goes to sleep, waiting for the signal of buffer dissatisfaction to come, we write again, when the buffer is empty, we read. Threads go to sleep, waiting for the non-empty buffer signal to come, we read again, because of the problem of thread synchronization, there is competition among multiple read and write threads, that is, these threads have mutually exclusive access, and the speed of read and write is different, so we need our mutually exclusive lock to lock multithreads, so that threads can execute one by one.

So the question arises. Why do so many things need to be done in a simple read-write operation?

  1. decoupling
    Suppose that producers and consumers are classified into two categories. If the producer invokes a method of the consumer directly, the producer will depend on the consumer (that is, coupling). In the future, if the consumer code changes, it may affect the producer. If both of them depend on a buffer, they do not depend directly on each other, and the coupling decreases accordingly.

  2. Another drawback is that producers directly invoke a method of consumers. Because function calls are synchronous (or blocked), producers have to wait there until the consumer's method returns. If consumers process data slowly, producers will waste their time.
    After using the producer/consumer model, the producer and consumer can be two independent concurrent principal producers who throw the generated data into the buffer and then produce the next data. Basically, it does not depend on the processing speed of consumers.

3. Buffers have another advantage. If the speed of manufacturing data is slow or fast, the benefits of buffers are reflected. When data is manufactured quickly, consumers can't process it. Unprocessed data can be temporarily stored in a buffer. When the producer's manufacturing speed slows down, the consumer slowly disposes of it.

So much is said about these basic concepts. Here are some ideas and our procedures.

Producer process:

1. Call pthread_mutex_lock() to lock the lock, and judge whether the buffer is full according to the following conditions;
(writepos + 1) % BUFSIZE == readpos
2. If full, call ptread_cond_wait() to enter the blocking and wait for the notful condition variable.
3. Write data and move write pointer writepos;
4. Call pthread_cond_signal() to signal to consumers through notempty conditional variables;
5. Call pthread_mutex_unlock() to unlock mutex.

Consumer process:

1. Call pthread_mutex_lock() to lock the lock, and judge whether the buffer is empty according to the following conditions;
writepos == readpos
2. If null, call ptread_cond_wait() to enter the blocking and wait for the notempty condition variable.
3. Read data and move read pointer readpos;
4. Call pthread_cond_signal() to signal to consumers through notful conditional variables.
5. Call pthread_mutex_unlock() to unlock mutex.

Thread Management Related Functions
int pthread_create( );
int pthread_join();
Thread Mutual Exclusion Control Related Functions
int pthread_mutex_init();
int pthread_mutex_lock();
int pthread_mutex_unlock();
Thread conditional variable control correlation function
int pthread_cond_init();
int pthread_cond_wait();
int pthread_cond_signal()

/*********************************************************************************
 *      Copyright:  (C) 2017 tangyanjun<519656780@qq.com>
 *                  All rights reserved.
 *
 *       Filename:  pro_Cus.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(08/13/2017)
 *         Author:  tangyanjun <519656780@qq.com>
 *      ChangeLog:  1, Release initial version on "08/13/2017 08:35:20 PM"
 *                 
 ********************************************************************************/
#include <stdio.h>
#include <pthread.h>
#define   BUFSIZE   1000
#define   OVER      (-1)

struct producers{
    int buffer[BUFSIZE];
    pthread_mutex_t  lock;   //mutex
    int readpos;
    int writepos;
    pthread_cond_t  notempty;  //Buffer Non-empty Conditions Judgment
    pthread_cond_t  notfull;   //Judgment of Buffer Unfullness Conditions
};

struct producers buffer;


void init(struct producers *b)
{
    pthread_mutex_init(&b->lock, NULL);
    pthread_cond_init(&b->notempty, NULL);
    pthread_cond_init(&b->notfull, NULL);
    b->readpos = 0;
    b->writepos = 0;
}

void put(struct producers* b, int data)
{
    pthread_mutex_lock(&b->lock); //If mutex is locked, the producer thread is suspended and returned

    if((b->writepos + 1) % BUFSIZE == b->readpos)   //Waiting for Buffer Full
    {
        pthread_cond_wait(&b->notfull, &b->lock);  //When the buffer is full, the producer will be suspended until it is awakened again.
    }

    b->buffer[b->writepos] = data;  //Writing data
    b->writepos++;     //Mobile pointer

    if(b->writepos >= BUFSIZE)
    {
        b->writepos = 0;
    }

    pthread_cond_signal(&b->notempty);  //Setting Conditional Variables with Buffer Non-empty
    pthread_mutex_unlock(&b->lock);
}

int get(struct producers *b)
{
    int data;
    pthread_mutex_lock(&b->lock);

    if(b->writepos == b->readpos)
    {
        pthread_cond_wait(&b->notempty, &b->lock); //Waiting Buffer Not Empty
    }

    data = b->buffer[b->readpos];   //Read data
    b->readpos++;      //Mobile pointer

    if(b->readpos >= BUFSIZE)
    {
        b->readpos = 0;
    }

    pthread_cond_signal(&b->notfull);   //Setting Conditional Variables with Unfull Buffers
    pthread_mutex_unlock(&b->lock);

    return data;
}

void *producer(void *data)
{
    int n;
    for(n = 0; n < 14; n++)
    {
        printf("[%d] ", n);
        put(&buffer, n);
    }
    put(&buffer, OVER);
    return NULL;
}

void *consumer(void *data)
{
    int d;
    while(1)
    {
        d = get(&buffer);
        if(d == OVER)
        {
            break;
        }
        printf("(%d) ", d);
    }
    return NULL;
}

int main()
{
    int i = 0;
    pthread_t th_a, th_b, th_c, th_d;
    void *retval;
    init(&buffer);
    pthread_create(&th_a, NULL, producer, 0);
    pthread_create(&th_b, NULL, consumer, 0);
    pthread_create(&th_a, NULL, producer, 0);
    pthread_create(&th_b, NULL, consumer, 0);

    pthread_join(th_a, &retval);
    pthread_join(th_b, &retval);
    printf("\n");
    return 0;
}

According to our results, we can see that the two threads of producer and consumer are indeed crossing, competing for resources. Due to time reasons, there is no in-depth study at this stage, and we will do a good job in the future.

Keywords: Mobile Programming

Added by cityboy101 on Tue, 04 Jun 2019 23:41:23 +0300