Here is my code. The sum part is not working. I need some controls for that but I couldn’t do it.
I tried make number variable number=-1
, but that also didn’t work and I had an infinite loop.
By the way this is my first question that i ask here, so i can make mistakes. Sorry for that.
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
#include<stdbool.h>
int number = 0, sum = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void *threadFunction1()
{
pthread_mutex_lock(&mutex);
while (true)
{
printf("Number:");
scanf("%d", &number);
if (number == 0) break;
}
pthread_exit(NULL);
}
void *threadFunction2()
{
pthread_mutex_lock(&mutex);
while (true)
{
sum += number;
printf("Sum is:%d\n", sum);
if (number == 0) break;
}
pthread_mutex_unlock(&mutex);
pthread_exit(NULL);
}
int main()
{
pthread_t thread1, thread2;
int case1, case2;
case1 = pthread_create(&thread1, NULL, threadFunction1, NULL);
case2 = pthread_create(&thread2, NULL, threadFunction2, NULL);
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
printf("It's over..\n");
pthread_mutex_destroy(&mutex);
return 0;
}
Here is the output
An immediately obvious issue is that threadFunction1
locks the mutex
pthread_mutex_lock(&mutex);
while (true)
{
printf("Number:");
scanf("%d", &number);
if (number == 0) break;
}
pthread_exit(NULL);
but never unlocks it. It is impossible to share a resource when one thread takes complete control over it.
The next issue is that the locking and unlocking of the mutex occurs outside of the while
loop.
pthread_mutex_lock(&mutex);
while (true)
{
sum += number;
printf("Sum is:%d\n", sum);
if (number == 0) break;
}
pthread_mutex_unlock(&mutex);
This means whichever thread gets CPU time first will lock the mutex, and enter a potentially long running loop, only unlocking the mutex when that loop ends:
If threadFunction1
is the first to lock the mutex, this loop lasts until the user enters 0
, wherein the loop is broken and the thread is terminated. After this, threadFunction2
never gets a turn, due to the lack of an unlock.
If threadFunction2
is the first to lock the mutex, this loop is broken after during its first iteration, as number
is initialized to 0
. After this the thread is terminated, and threadFunction1
would get a turn, since the mutex is unlocked.
The final issue is that a single mutex alone is not the correct tool to use here.
Even moving the locks and unlocks inside the loop, so that each thread has a chance to lock the mutex, do some work, and then unlock, there is no guarantee that both threads will get equal access to the lock.
while (true)
{
pthread_mutex_lock(&mutex);
sum += number;
printf("Sum is:%d\n", sum);
pthread_mutex_unlock(&mutex);
if (number == 0) break;
}
In fact, the opposite will probably occur. By default, it is very likely for one thread to be granted some CPU time, lock the mutex, do some work, unlock the mutex, and with its remaining time immediately lock the mutex again.
So we need a way to make two threads evenly share a resource.
One way is, instead of using a mutex, to use two semaphores to create a lockstep. This means one thread waits to for a semaphore which the other thread posts, and vice versa.
Note that in main
we start the to_read
semaphore with a value of 1
, meaning the scanning
thread effectively “goes first”.
#include <pthread.h>
#include <semaphore.h>
#include <stdbool.h>
#include <stdio.h>
#include <unistd.h>
static int number = 0;
static int sum = 0;
static sem_t to_read;
static sem_t to_write;
static void *scanning(void *arg) {
while (true) {
sem_wait(&to_read);
printf("Enter a number: ");
if (1 != scanf("%d", &number))
number = 0;
sem_post(&to_write);
if (number == 0)
break;
}
return NULL;
}
static void *adding(void *arg) {
while (true) {
sem_wait(&to_write);
sum += number;
printf("Sum is: %d\n", sum);
sem_post(&to_read);
if (number == 0)
break;
}
return NULL;
}
int main(void) {
pthread_t thread1, thread2;
sem_init(&to_read, 0, 1);
sem_init(&to_write, 0, 0);
pthread_create(&thread1, NULL, scanning, NULL);
pthread_create(&thread2, NULL, adding, NULL);
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
puts("It's over...");
sem_destroy(&to_read);
sem_destroy(&to_write);
}
Note that this will surely perform worse than a single threaded loop in main, doing the same amount of work, due to the overhead of context switching. That said, it is just a toy example.