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);