Ahmed Sabry
Ahmed Sabry

Reputation: 11

Should we use mutex with semaphore to make a correct synchronization and to prevent a race condition?

I am trying to see the race condition happens in the comsumer-producser problem, so I made multiple producers and mulitple consumers.

From what I know that I need to provide mutex with semaphore: Mutex for the race conditions, because muliple producers can access the buffer at the same time. then the data might be corrupted.

And semaphore to provide signaling between the producers and the consumers

The problem here that the sync is happening correctly while I am not using the Mutex (i am using the Semaphore only). is my understanding correct or is there anything wrong to do in the code below:

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

int buffer;
int loops = 0;

sem_t empty;
sem_t full;
sem_t mutex; //Adding MUTEX

void put(int value) {
    buffer = value;       
}

int get() {
    int b = buffer;      
    return b;
}



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

        //sem_wait(&mutex);
        put(i);
        //printf("Data Set from %s, Data=%d\n", (char*) arg, i);
        //sem_post(&mutex);

        sem_post(&full);
    }
}

void *consumer(void *arg) {
    int i;
    for (i = 0; i < loops; i++) {
        sem_wait(&full);

        //sem_wait(&mutex);
        int b = get();
        //printf("Data recieved from %s, %d\n", (char*) arg, b);
        printf("%d\n", b);
        //sem_post(&mutex);

        sem_post(&empty);

    }
}

int main(int argc, char *argv[])
{
    if(argc < 2 ){
        printf("Needs 2nd arg for loop count variable.\n");
        return 1;
    }

    loops = atoi(argv[1]);

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

    pthread_t pThreads[3];
    pthread_t cThreads[3];


    pthread_create(&cThreads[0], 0, consumer, (void*)"Consumer1");
    pthread_create(&cThreads[1], 0, consumer, (void*)"Consumer2");
    pthread_create(&cThreads[2], 0, consumer, (void*)"Consumer3");

    //Passing the name of the thread as paramter, Ignore attr
    pthread_create(&pThreads[0], 0, producer, (void*)"Producer1");
    pthread_create(&pThreads[1], 0, producer, (void*)"Producer2");
    pthread_create(&pThreads[2], 0, producer, (void*)"Producer3");


    pthread_join(pThreads[0], NULL);
    pthread_join(pThreads[1], NULL);
    pthread_join(pThreads[2], NULL);

    pthread_join(cThreads[0], NULL);
    pthread_join(cThreads[1], NULL);
    pthread_join(cThreads[2], NULL);

    return 0;
}

Upvotes: 1

Views: 310

Answers (1)

gmelodie
gmelodie

Reputation: 452

I believe I have the problem figured out. Here's what is happening

When initializing your semaphores you set empty's number of threads to 1 and full's to 0

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

This means that there is only one "space" for the thread to get into the critical region. In other words, what your program is doing is

produce (empty is now 0, full has 1)
consume (full is now 0, empty has 0)
produce (empty is now 0, full has 1)
...

It's as if you had a token (or, if you like, a mutex), and you pass that token between consumers and producers. That is actually what the consumer-producer problem is all about, only that in most cases we are worried about having several consumers and producers working at the same time (which means you have more than one token). Here, because you have only one token, you basically have what one mutex would do.

Hope it helped :)

Upvotes: 1

Related Questions