Threading and inter-thread communication are complex topics that can be challenging to implement (correctly). However, even if you can use concepts like mutexes and semaphores without errors, it’s still not always trivial to know how to apply them to a specific problem. Even for the well-known producer-consumer problem, several different setups are possible.
Below, you will find an implementation variation on the prime-generator producer-consumer problem we handled during class (especially “Producer-Consumer V4: Semaphores” in Section 6.7). As you will see, it is fundamentally different from the lab exercises in how it handles producer/consumer responsibilities.
This task requires you to compare both approaches, to think about the bigger picture, and forces you to explain your thoughts in a concise and clear fashion. Some of the questions do not have a “single right answer” and/or are a bit vague: that is intentional.
Analyse the code below and write a report (3-4 pages):
Note that, like all other tasks, this is an individual assignment!
Still, it’s possible that you’ve solved “Producer-Consumer V4: Semaphores” in group during class, which was allowed/recommended.
To be able to make sure however that each individual student really understands what the code does (and didn’t just copy-paste it from someone else), you MUST add sufficient personal comments in the code explaining the most important parts (how they work and why that approach was chosen).
Note that this all only counts for “Producer-Consumer V4: Semaphores” : questions 1-5 above MUST be answered fully individually!
Hand in the report (as a .pdf file!) and the source code for the “Producer-Consumer V4” exercise, together in a .zip file, via Toledo. Name it: BESC_task3_firstName_lastName.zip.
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#define NUMBER_OF_THREADS 5
#define MAX_COUNT 500000
static sem_t obj_produced;
static sem_t obj_consumed;
static int shelf;
void * producer() {
int i;
for(i=2; i< MAX_COUNT; i++) {
shelf = i;
sem_post(&obj_produced);
sem_wait(&obj_consumed);
}
return NULL;
}
void * consumer() {
unsigned char isPrime;
int i;
int VUT;
while(1) {
sem_wait(&obj_produced);
VUT = shelf;
sem_post(&obj_consumed);
printf("[CONSUMER] %d\n", VUT);
isPrime = 1;
for (i=2;i<VUT; i++) {
if (VUT % i ==0) {
isPrime = 0;
}
}
if(isPrime==1) {
printf(" thread #x announces that %d is prime.\n", i);
}
}
}
int main(void) {
int i = 0, err;
pthread_t tid[NUMBER_OF_THREADS];
// create semaphores
err = sem_init(&obj_produced, 0, 0);
if(err != 0) {
printf("\ncan't create semaphore: obj_produced [%s]", strerror(err));
return 1;
}
err = sem_init(&obj_consumed, 0, 0);
if(err != 0) {
printf("\ncan't create semaphore: obj_produced [%s]", strerror(err));
return 1;
}
// create producer thread
err = pthread_create(&(tid[i]), NULL, &producer, NULL);
if (err != 0) {
printf("\ncan't create producer thread: [%s]", strerror(err));
return 1;
}
printf("Producer thread created\n");
// create consumer threads
for(i=1;i<NUMBER_OF_THREADS;i++) {
err = pthread_create(&(tid[i]), NULL, &consumer, NULL);
if (err != 0) {
printf("\ncan't create consumer thread %d: [%s]", i, strerror(err));
}
printf("Consumer thread %d created\n", i);
}
// wait for producer thread
pthread_join(tid[0], NULL);
// kill consumer threads
for(i=1;i<NUMBER_OF_THREADS;i++) {
pthread_kill(tid[i], 9);
}
// delete the semaphores
sem_destroy(&obj_produced);
sem_destroy(&obj_consumed);
return 0;
}