I have two threads
xThread
: Continuously Prints X on the console
inputThread
: Gets input from the stdin
The continuous printing stops when the user enters 'C' or 'c'
#include<stdio.h>
#include<sys/select.h>
#include<pthread.h>
#define S sleep(0)
int read_c = 0;
pthread_mutex_t read_c_mutex = PTHREAD_MUTEX_INITIALIZER;
void* inputThread_fn(void* arg)
{
char inputChar;
while(1)
{
S;
printf("\nChecking input");
scanf("%c",&inputChar);
if(inputChar=='C' || inputChar == 'c')
{
pthread_mutex_trylock(&read_c_mutex); /*<--This must be _lock ?
because with the use of trylock even If i don't aquire a lock I go ahead and modify
the variable?*/
read_c = 1;
pthread_mutex_unlock(&read_c_mutex);
pthread_exit(NULL);
}
}
}
void* xThread_fn(void* arg)
{
while(1)
{
S;
pthread_mutex_trylock(&read_c_mutex);
if(!read_c)
printf(" X");
else
pthread_exit(NULL);
pthread_mutex_unlock(&read_c_mutex);
}
}
void* yThread_fn(void* arg)
{
while(1)
{
S;
pthread_mutex_trylock(&read_c_mutex);
if(!read_c)
printf(" Y");
else
pthread_exit(NULL);
pthread_mutex_unlock(&read_c_mutex);
}
}
int main()
{
pthread_t xThread,yThread,inputThread;
pthread_create(&xThread,NULL,xThread_fn,NULL);
pthread_create(&inputThread,NULL,inputThread_fn,NULL);
pthread_join(xThread,NULL);
pthread_join(inputThread,NULL);
return 0;
}
When I use sleep(1)
the threads are spawned and [irrespective of which thread is started first] when the program reaches scanf
in inputThread
it halts for the user input and the code does not proceed until I enter an input.
When I execute the code with sleep(0)
, scanf
does not halt for the input, it keeps printing 'X' until I enter 'C' or 'c'
Does sleep()
interfere with scanf
in someway?
Note: I am aware of select
being used for non-blocking input. I have tried the same too and the code runs fine. I just want to know in the above case why inconsistent behaviour arises?
Update (Using trylock
)
void* inputThread_fn(void* arg)
{
char inputChar;
while(1)
{
S;
scanf("%c",&inputChar);
if(inputChar=='C' || inputChar == 'c')
{
pthread_mutex_trylock(&read_c_mutex);
read_c = 1;
pthread_mutex_unlock(&read_c_mutex);
pthread_exit(NULL);
}
}
}
void* xThread_fn(void* arg)
{
while(1)
{
S;
pthread_mutex_trylock(&read_c_mutex);
if(!read_c)
{
pthread_mutex_unlock(&read_c_mutex);
printf(" X");
}
else
{
pthread_mutex_unlock(&read_c_mutex);
pthread_exit(NULL);
}
fflush(stdout);
}
}
void* yThread_fn(void* arg)
{
while(1)
{
S;
pthread_mutex_trylock(&read_c_mutex);
if(!read_c)
{
pthread_mutex_unlock(&read_c_mutex);
printf(" Z");
fflush(stdout);
}
else
{
pthread_mutex_unlock(&read_c_mutex);
pthread_exit(NULL);
}
}
}
The reason you don't see output is because you're not flushing the buffer.
The reason you don't need to flush the buffer with
sleep(0)
is because the writer thread writes so much data that the buffer fills up and is automatically flushed.Don't use
pthread_mutex_trylock()
Don't use
pthread_mutex_trylock()
here. It's wrong.The difference between
lock()
andtrylock()
is thatlock()
will always succeed1 buttrylock()
will sometimes fail. That's why it's called "try".Since
trylock()
sometimes fails, you have to handle the case where it failed. Your code doesn't handle the case: it simply plows forward, pretending it acquired the lock. So, supposetrylock()
doesn't lock the mutex. What happens?Then there's the question of how the code should handle
trylock()
failing. If you can't answer this question, then the default answer is "uselock()
".In the reader thread, you can't use
trylock()
because you have to lock the mutex:In the writer thread, there's no point in using
trylock()
:However, this is entirely pointless. The only reason
trylock()
will fail in the writer thread is if the reader thread owns the lock, which only happens if it is currently in the process of settingread_c = 1;
. So you might as well wait for it to finish, since you know you're going to exit anyway (why write more output after you know that the user has signaled your program to stop?)Just use
lock()
. You'll uselock()
99% of the time, andtrylock()
is for the other 1%.1: The
lock()
function can fail, but this usually means you've misused the mutex.Misconceptions about
lock()
andtrylock()
You said this about
trylock()
:I think there is a very fundamental misunderstanding here about the nature of mutexes. If another thread weren't accessing the variable at the same time, then you wouldn't need a mutex at all.
Suppose you're doing important work at the office, and you need to use the photocopier. Only one person can use the photocopier at a time. You go to the photocopier and someone's already using it.
If you wait in line until it's your turn, then that's
lock()
.If you give up and go back to your desk, then that's
trylock()
. (Your program actually ignores the return code fortrylock()
, so you basically start mashing buttons on the photocopier even if someone else is using it.)Now imagine that it takes one minute to use the photocopier, only two people ever use the photocopier, and they only use the photocopier once every twenty years.
If you use
lock()
, then you wait in line for at most one minute before using the photocopier.If you use
trylock()
, then you give up and go back to your desk and wait twenty years before trying the photocopier again.It doesn't make any sense to use
trylock()
, does it? Are your threads so impatient that they can't spend even one minute in line once every twenty years?Now your boss comes down and said, "Where is that report I asked you to photocopy?" And you say, "Well, I went to the photocopier six years ago but someone was using it."
The numbers (one minute every twenty years) are based on Latency Numbers Every Programmer Should Know, where it notes that locking/unlocking a mutex is about 25ns. So if we pretend that it takes one minute to lock and then unlock a mutex, then
sleep(1)
causes the thread to wait for twenty years.