Skip to content
Advertisement

Multithreading with Semaphor, Mutex and PThread

Me and a friend are currently working on basic multithreading examples for university in c. We’re supposed to solve the producer/consumer problem with a multithreaded buffer. We’ve got a working version using mutex and conditional variables, but trying to solve this using semaphores and mutex were having three major problems.

Problem 1: If we start the consumer first he sometimes randomly consumes an invalid char and crashes.

Problem 2: If we start the producer first, he sometimes doesnt produce any chars until the consumer is started, which leads to problem 1.

Problem 3: Our producers dont fill the whole buffer, after every insertion in the buffer the consumer is consuming, no matter how many producers there are.

According to our given pseudocode-examples atleast problem 2&3 shouldnt be existing. Im really grateful for any answers, since Im not able to find the mistake at the moment.

Consumer:

void *consumer_sem(void *args) {
    printf("cons startedn");
    char c;

    while (cons_running) {
        sem_wait(occupied);
        pthread_mutex_lock(&mutex);

        c = consume();

        pthread_mutex_unlock(&mutex);
        sem_post(free);

        printf("consumer consumed %cnn", c);
        sleep(2);
    }   
}

Producer:

void *producer1_sem(void *args) {    
    printf("prod1 startedn");    
    char c;    
    int index=0;

    while (prod1_running) {    
        c = lowercase[index];    
        index=next(index);    

        sem_wait(free);
        pthread_mutex_lock(&mutex);

        add(c);

        pthread_mutex_unlock(&mutex);    
        sem_post(occupied);

        printf("producer1 produced something!n");    
        printf("%d elements in buffernn",getElemsInBuffer());   
        sleep(3);    
    }    
}

main:

sem_t *occupied, *free;

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

int main(void) {
    occupied=sem_open("/occupied", O_CREAT, 0644, 0);
    free=sem_open("/free", O_CREAT, 0644, BUFFER_SIZE);

    //some unrelated code called

    pthread_create(&thread_ids[0], NULL, producer1_sem, NULL);
    pthread_create(&thread_ids[1], NULL, producer2_sem, NULL);
    pthread_create(&thread_ids[2], NULL, consumer_cond, NULL);

}

Advertisement

Answer

Function sem_open creates global (named) semaphore, which exists until sem_unlink() is called.

When you run your program second time (and further), sem_open reuses already existed semaphore, and its value isn’t reseted. You can easily detect that:

// With O_EXCL opening already existed semaphore will fail.
occupied=sem_open("/occupied", O_CREAT | O_EXCL, 0644, 0);
if(occupied == SEM_FAILED) {
    perror("Failed to create new semaphore");
    exit(1);
}

Actually, when a semaphore is used only by single process (but many threads), it is sufficient to initialize it with sem_init:

sem_t occupied;
//...
int main()
{
    sem_init(&occupied, 0 /* do not share between processes*/, 0 /* initial value*/);
    //...
}

Alternatively, you may destroy old semaphore before attempt to create new one:

// Destroy the semaphore if it exists.
sem_unlink("/occupied");
// Create the semaphore again.
occupied = sem_open("/occupied", O_CREAT | O_EXCL, 0644, 0);
User contributions licensed under: CC BY-SA
4 People found this is helpful
Advertisement