Consider the following program: #include <pthread.h> #include <stdio.h> #include
ID: 3848196 • Letter: C
Question
Consider the following program:
#include <pthread.h>
#include <stdio.h>
#include <errno.h>
#define MAX_RESOURCES 100
int available_resources = MAX_RESOURCES;
int decrease_count(int count) {
if (available_resources < count) {
return -1;
}
else {
available_resources -= count;
return 0;
}
}
int increase_count(int count) {
available_resources += count;
return 0;
}
void *runner(void *param); /* the thread */
int main(int argc, char *argv[]) {
pthread_t tid1, tid2, tid3, tid4; /* the thread identifiers*/
pthread_attr_t attr; /* set of attributes for the thread */
/* get the default attributes */
pthread_attr_init(&attr);
/* create the threads */
pthread_create(&tid1,&attr,runner,NULL);
pthread_create(&tid2,&attr,runner,NULL);
pthread_create(&tid3,&attr,runner,NULL);
pthread_create(&tid4,&attr,runner,NULL);
/* now wait for the thread to exit */
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
pthread_join(tid3,NULL);
pthread_join(tid4,NULL);
printf("available_resources: %d ", available_resources);
}
/**
* The thread will begin control in this function
*/
void *runner(void *param) {
int i;
for (i=1; i<= 1000000; i++) {
if (decrease_count(3) == 0) {
increase_count(3);
}
}
pthread_exit(0);
}
Study the code and notice that logically after running the program, the available_resources variable should have the initial value of 100.
Translate and run the program several times. You will notice that the result that prints the value of the available_resources variable can be changed. This is considered a misconduct and is a consequence of the lack of some synchronization method.
Create a new version of the program by solving the problem by using binary semaphores and mutex locks.
Run the new version several times to check if the problem is resolved.
Explanation / Answer
Hi,
First, lets understand whats wrong in the above posted code, in the code we are using multiple threads and all are used to modify the variable available_resources, without any kinds of synchronization . Think of this like, there is like a shared resource and you are trying to modify simultanously when somone else is working on it that too without any regard to what the the other thread is doing. This is called the synchronization problem and it can be solved used Mutex and Binary Semaphore.
1. Mutex: Its a simple intutive of way of solving this problem, in simple words this means, whenver you are modifying a shared resource, first acquire a lock(basically tell everyone you are working on this) and then do whatever you are supposed to do and then unlock(releasing and telling others that i am done)
This can be implied by using the mutext type 'pthread_mutex_t' . here is the complete code using mutex
#include <pthread.h>
#include <stdio.h>
#include <errno.h>
#define MAX_RESOURCES 100
int available_resources = MAX_RESOURCES;
pthread_mutex_t mutex; // declare the mutex
int decrease_count(int count) {
pthread_mutex_lock(&mutex); // First acquire a mutext lock
if (available_resources < count) {
pthread_mutex_unlock(&mutex); // Release the lock acquired
return -1;
}
else {
available_resources -= count;
pthread_mutex_unlock(&mutex);
return 0;
}
}
int increase_count(int count) {
pthread_mutex_lock(&mutex);
available_resources += count;
pthread_mutex_unlock(&mutex);
return 0;
}
void *runner(void *param); /* the thread */
int main(int argc, char *argv[]) {
pthread_t tid1, tid2, tid3, tid4; /* the thread identifier */
pthread_attr_t attr; /* set of attributes for the thread */
/* get the default attributes */
pthread_attr_init(&attr);
/* create the thread */
pthread_create(&tid1,&attr,runner,argv[1]);
pthread_create(&tid2,&attr,runner,argv[1]);
pthread_create(&tid3,&attr,runner,argv[1]);
pthread_create(&tid4,&attr,runner,argv[1]);
/* now wait for the thread to exit */
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
pthread_join(tid3,NULL);
pthread_join(tid4,NULL);
printf("available_resources: %d ", available_resources);
}
/**
* The thread will begin control in this function
*/
void *runner(void *param) {
int i;
for (i=1; i<= 1000000; i++) {
if (decrease_count(3) == 0) {
increase_count(3);
}
}
pthread_exit(0);
}
2. Binary semaphore: This is another solution to solve the synchronization problem. Binary semaphores are binary, they can have two values only; one to represent that a process/thread is in the critical section(code that access the shared resource) and others should wait, the other indicating the critical section is free.
Here is the full implementation of the above code using semaphores
#include <pthread.h>
#include <stdio.h>
#include <semaphore.h>
#define MAX_RESOURCES 100
int available_resources = MAX_RESOURCES;
sem_t semaphore;
int decrease_count(int count) {
if (sem_wait(&semaphore) != 0) { // check if more than 2 threads is not working on this
printf("%s","2 more threads already working on this variabe");
return -1;
}
if (available_resources < count) {
if (sem_post(&semaphore) != 0) {
printf("%s","2 more threads already working on this variabe");
return -1;
}
return -1;
}
else {
available_resources -= count;
if (sem_post(&semaphore) != 0) {
printf("%s","2 more threads already working on this variabe");
return -1;
}
return 0;
}
}
int increase_count(int count) {
if (sem_wait(&semaphore) != 0) {
printf("%s","2 more threads already working on this variabe");
return -1;
}
available_resources += count;
if (sem_post(&semaphore) != 0) {
printf("%s","2 more threads already working on this variabe");
return -1;
}
return 0;
}
void *runner(void *param); /* the thread */
int main(int argc, char *argv[]) {
pthread_t tid1, tid2, tid3, tid4; /* the thread identifier */
pthread_attr_t attr; /* set of attributes for the thread */
if (sem_init(&semaphore,0,1) == -1) { // since it is a binary semaphore we keep only 2 values
printf("%s","2 more threads already working on this variabe");
return -1;
}
/* get the default attributes */
pthread_attr_init(&attr);
/* create the thread */
pthread_create(&tid1,&attr,runner,argv[1]);
pthread_create(&tid2,&attr,runner,argv[1]);
pthread_create(&tid3,&attr,runner,argv[1]);
pthread_create(&tid4,&attr,runner,argv[1]);
/* now wait for the thread to exit */
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
pthread_join(tid3,NULL);
pthread_join(tid4,NULL);
printf("available_resources: %d ", available_resources);
}
/**
* The thread will begin control in this function
*/
void *runner(void *param) {
int i;
for (i=1; i<= 1000000; i++) {
if (decrease_count(3) == 0) {
increase_count(3);
}
}
pthread_exit(0);
}
Thumbs up if this was helpful, otherwise let me know in comments
Good Day.