Hi i'm new to C and trying to understand mutex, condition and threads. I have the basics of how thread works. Correct me if i'm wrong, from what i understand here is that one thread is waiting on the other 2 thread to send a signal to wake him up. The code can only be run by one thread at a time because of the mutex, thread 2 acquires the lock and runs the code, the sleep(1) helps to alternate thread so thread 2 doesn't run it again, and so thread 3 acquires the lock and runs the code and so on. Only the thread that locks and release the mutex lock. Once it reaches the COUNT_LIMIT 12, it sends a signal to the waiting thread which is thread 1. I understand up till here if i didn't misunderstood anything
So my question is that this few pieces of code here that is in void *watch_count:
pthread_mutex_lock(&count_mutex);
pthread_cond_wait(&count_threshold_cv, &count_mutex);
pthread_mutex_unlock(&count_mutex);
Is it acquiring the same lock as the one in void *inc_count? If so it locks the mutex and the pthread_cond_wait is waiting for the signal in void *watch_count, then continues to execute the code? What is the pthread_mutex_lock and pthread_mutex_unlock doing in void *watch_count, does this play a part in stopping the threads 2 and 3 from executing while waiting for thread 1 to finish it's job?
I hope i am making sense or exaplaining what i understand correctly, thanks in advance for any advice given!
#define NUM_THREADS 3
#define TCOUNT 10
#define COUNT_LIMIT 12
int count = 0;
pthread_mutex_t count_mutex;
pthread_cond_t count_threshold_cv;
void *inc_count(void *t)
{
int i;
long my_id = (long)t;
for (i=0; i < TCOUNT; i++) {
pthread_mutex_lock(&count_mutex);
count++;
/*
Check the value of count and signal waiting thread when condition is
reached. Note that this occurs while mutex is locked.
*/
if (count == COUNT_LIMIT) {
printf("inc_count(): thread %ld, count = %d Threshold reached. ",
my_id, count);
pthread_cond_signal(&count_threshold_cv);
printf("Just sent signal.\n");
}
printf("inc_count(): thread %ld, count = %d, unlocking mutex, i: %d\n",
my_id, count, i);
pthread_mutex_unlock(&count_mutex);
/* Do some work so threads can alternate on mutex lock */
sleep(1);
}
pthread_exit(NULL);
}
void *watch_count(void *t)
{
long my_id = (long)t;
printf("Starting watch_count(): thread %ld\n", my_id);
pthread_mutex_lock(&count_mutex);
while (count < COUNT_LIMIT) {
printf("watch_count(): thread %ld Count= %d. Going into wait...\n", my_id,count);
pthread_cond_wait(&count_threshold_cv, &count_mutex);
printf("watch_count(): thread %ld Condition signal received. Count= %d\n", my_id,count);
printf("watch_count(): thread %ld Updating the value of count...\n", my_id,count);
count += 125;
printf("watch_count(): thread %ld count now = %d.\n", my_id, count);
}
printf("watch_count(): thread %ld Unlocking mutex.\n", my_id);
pthread_mutex_unlock(&count_mutex);
pthread_exit(NULL);
}
int main(int argc, char *argv[])
{
int i, rc;
long t1=1, t2=2, t3=3;
pthread_t threads[3];
pthread_attr_t attr;
/* Initialize mutex and condition variable objects */
pthread_mutex_init(&count_mutex, NULL);
pthread_cond_init (&count_threshold_cv, NULL);
/* For portability, explicitly create threads in a joinable state */
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
pthread_create(&threads[0], &attr, watch_count, (void *)t1);
pthread_create(&threads[1], &attr, inc_count, (void *)t2);
pthread_create(&threads[2], &attr, inc_count, (void *)t3);
/* Wait for all threads to complete */
for (i = 0; i < NUM_THREADS; i++) {
pthread_join(threads[i], NULL);
}
printf ("Main(): Waited and joined with %d threads. Final value of count = %d. Done.\n",
NUM_THREADS, count);
/* Clean up and exit */
pthread_attr_destroy(&attr);
pthread_mutex_destroy(&count_mutex);
pthread_cond_destroy(&count_threshold_cv);
pthread_exit (NULL);
}
Yes, all your threads are acquiring the same lock (there is only one mutex in this program - count_mutex).
When watch_count() calls pthread_cond_wait(), it unlocks the mutex and is suspended in one atomic action. This allows other threads to acquire the lock. When the thread that called pthread_cond_wait() wakes up, it will re-acquire the lock before pthread_cond_wait() returns (this means that if another thread has the mutex locked, pthread_cond_wait() can't return until that other thread releases the lock first).
You must have the mutex locked when you call pthread_cond_wait() - this is a requirement of the API. Furthermore, the watch_count() thread locks the count_mutex so that it can access the count variable without introducing a race condition (a concurrent modification of count from another thread). Notice that none of the threads access the count variable - either for read or write - without having the count_mutex locked at the time.
Related
The thread calling pthread_cond_signal is re-grabbing the mutex before the signaled thread can be released.
The code below shows a simple example of the issue at hand. The main thread will hold the lock, create the worker thread, and then enter a loop that prints data as it comes in. It is signaled to run via a conditional variable.
The worker thread will enter a loop that generates data, grabs the lock, writes the data, and then signals the main thread.
#include <stdio.h>
#include <pthread.h>
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int data = 0;
void *thread(void *arg) {
int length = *(int *) arg;
for (int i = 0; i < length; i++) {
// Do some work
pthread_mutex_lock(&lock);
data = i;
fprintf(stdout, "Writing\n");
pthread_cond_signal(&cond);
pthread_mutex_unlock(&lock);
}
pthread_exit(0);
}
int main(int argc, const char *argv[]) {
pthread_t worker;
int length = 4;
pthread_mutex_lock(&lock);
pthread_create(&worker, 0, thread, &length);
for (int i = 0; i < length; i++) {
fprintf(stdout, "Waiting\n");
pthread_cond_wait(&cond, &lock);
fprintf(stdout, "read data: %d\n", data);
}
pthread_mutex_unlock(&lock);
pthread_join(worker, NULL);
return 0;
}
This will give the following output:
Waiting
Writing
Writing
Writing
Writing
read data: 3
Waiting
Expected behavior:
Main thread holds the mutex and only releases it once its waiting. The worker thread will then write its data and signal the main thread. The main thread will immediately lock the mutex on signal, then read the data and go back to waiting, releasing the mutex. Meanwhile the worker thread will do its work and be waiting until the main thread is waiting again to write its data and signal it.
Instead, it appears that the worker thread is getting the mutex immediately after calling signal, rarely letting the main thread get access. If I put a sleep in the worker thread in place of the // Do some work then it will give the expected output.
Signalling the condition variable does not give any kind of priority on locking the mutex to a thread that was waiting on that condition variable. All it means is that at least one thread waiting on the condition variable will start trying to acquire the mutex so it can return from the pthread_cond_wait(). The signalling thread will keep executing and can easily re-acquire the mutex first, as you've seen.
You should never have a condition variable without an actual condition over some shared state that you're waiting for - returning from a pthread_cond_wait() doesn't mean a thread should definitely proceed, it means that it should check if the condition it was waiting for is true. That's why they're called condition variables.
In this case, the state your writing thread wants to wait for is "the main thread has consumed the last data I wrote.". However, your reading (main) thread also needs to wait on a condition - "the writing thread has written some new data". You can achieve both these conditions with a flag variable that indicates that some new, unconsumed data has been written to the data variable. The flag starts out unset, is set by the writing thread when it updates data, and is unset by the main thread when it reads from data. The writing thread waits for the flag to be unset, and the reading thread waits for the flag to be set.
With this arrangement, you also don't need to have the mutex locked when you start the writing thread - it doesn't matter which order the threads start, because everything is consistent either way.
The updated code looks like:
#include <stdio.h>
#include <pthread.h>
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int data = 0;
int data_available = 0;
void *thread(void *arg)
{
int length = *(int *) arg;
for (int i = 0; i < length; i++) {
// Do some work
pthread_mutex_lock(&lock);
fprintf(stdout, "Waiting to write\n");
while (data_available)
pthread_cond_wait(&cond, &lock);
fprintf(stdout, "Writing\n");
data = i;
data_available = 1;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&lock);
}
pthread_exit(0);
}
int main(int argc, const char *argv[])
{
pthread_t worker;
int length = 4;
pthread_create(&worker, 0, thread, &length);
for (int i = 0; i < length; i++) {
pthread_mutex_lock(&lock);
fprintf(stdout, "Waiting to read\n");
while (!data_available)
pthread_cond_wait(&cond, &lock);
fprintf(stdout, "read data: %d\n", data);
data_available = 0;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&lock);
}
pthread_join(worker, NULL);
return 0;
}
Of course, the threads end up working in lockstep - but essentially you have a producer-consumer with a maximum queue length of 1, so that's expected.
Building on my question from yesterday, here, I wrote a small code sample that starts a number of counting and a number of waiting threads.
The waiting threads are stopped pthread_cond_wait until they receive a signal. The signal is sent after the counting threads finish their tasks.
The waiting threads receive their signal and each thread prints out its given, unique id.
I would expect all waiting threads to receive the signal at the same time, so that each of them can proceed with the program. I noticed however, the outputs are not chaotic, in fact they even appear to be fairly ordered, like in FILO!
There are now various places, where I could have gone wrong.
Here is my code:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#define counting_threads 100
#define waiting_threads 100
int count = 0;
int counting_thread_ids[counting_threads];
int waiting_thread_ids[waiting_threads];
pthread_mutex_t count_mutex;
pthread_cond_t count_threshold_cv;
void init_ids(){
for(int i = 0; i < counting_threads; i++)
counting_thread_ids[i] = 2*i;
for(int j =0; j < waiting_threads; j++)
waiting_thread_ids[j] = 2*j+1;
}
void counting(void *t)
{
pthread_mutex_lock(&count_mutex);
count++;
if (count == counting_threads) {
sleep(2);
printf("inc_count(): count = %d Threshold reached. Signaling waiting threads. \n", count);
//~ pthread_cond_signal(&count_threshold_cv);
pthread_cond_broadcast(&count_threshold_cv);
}
pthread_mutex_unlock(&count_mutex);
}
void *waiting(void *t)
{
long my_id = (long)t;
//~ printf("Starting watch_count(): thread %ld\n", my_id);
pthread_mutex_lock(&count_mutex);
//~ printf("watch_count(): I start waiting now: %ld \n", my_id);
pthread_cond_wait(&count_threshold_cv, &count_mutex);
printf("watch_count(): thread %ld Condition signal received.\n", my_id);
pthread_mutex_unlock(&count_mutex);
pthread_exit(NULL);
}
int main (int argc, char *argv[])
{
init_ids();
pthread_t wt[waiting_threads];
pthread_t ct[counting_threads];
/* Initialize mutex and condition variable objects */
pthread_mutex_init(&count_mutex, NULL);
pthread_cond_init (&count_threshold_cv, NULL);
for(int i = 0; i < waiting_threads; i++)
pthread_create(&wt[i], NULL, waiting, (void*) waiting_thread_ids[i] );
for(int i = 0; i < counting_threads; i++)
pthread_create(&ct[i], NULL, counting, (void*) counting_thread_ids[i] );
/* Wait for all threads to complete */
for (int i=0; i<waiting_threads; i++) {
pthread_join(wt[i], NULL);
}
for (int i=0; i<counting_threads; i++) {
pthread_join(ct[i], NULL);
}
/* Clean up and exit */
pthread_mutex_destroy(&count_mutex);
pthread_cond_destroy(&count_threshold_cv);
pthread_exit(NULL);
}
The pthread_cond_signal() call unblocks at least one of the threads that are blocked on the specified condition variable cond (if any threads are blocked on cond).
The pthread_cond_broadcast() call unblocks all threads currently blocked on the specified condition variable cond.
If more than one thread is blocked on a condition variable, the scheduling policy determines the order in which threads are unblocked.
More information about the scheduling policies can be found here.
I have a threadpool of workers. Each worker executes this routine:
void* worker(void* args){
...
pthread_mutex_lock(&mtx);
while (queue == NULL && stop == 0){
pthread_cond_wait(&cond, &mtx);
}
el = pop(queue);
pthread_mutex_unlock(&mtx);
...
}
main thread:
int main(){
...
while (stop == 0){
...
pthread_mutex_lock(&mtx);
insert(queue, el);
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mtx);
...
}
...
}
Then I have a signal handler that executes this code when it receives a signal:
void exit_handler(){
stop = 1;
pthread_mutex_lock(&mtx);
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mtx);
}
I have omitted declarations and initialization, but the original code has them.
After a signal is received most of the time it's all ok, but sometimes it seems that some worker threads stay in the wait loop because they don't see that the variable stop is changed and/or they are not waken up by the broadcast.
So the threads never end.
What I am missing?
EDIT: stop=1 moved inside the critical section in exit_handler. The issue remains.
EDIT2: I was executing the program on a VM with Ubuntu. Since the code appears to be totally right I tried to change VM and OS (XUbuntu) and now it seems to work correctly. Still don't know why, anyone has an idea?
Some guessing here, but it's too long for a comment, so if this is wrong, I will delete. I think you may have a misconception about how pthread_cond_broadcast works (at least something I've been burned with in the past). From the man page:
The pthread_cond_broadcast() function shall unblock all threads
currently blocked on the specified condition variable cond.
Ok, that make sense, _broadcast awakens all threads currently blocked on cond. However, only one of the awakened threads will then be able to lock the mutex after they're all awoken. Also from the man page:
The thread(s) that are unblocked shall contend for the mutex according
to the scheduling policy (if applicable), and as if each had called
pthread_mutex_lock().
So this means that if 3 threads are blocked on cond and _broadcast is called, all 3 threads will wake up, but only 1 can grab the mutex. The other 2 will still be stuck in pthread_cond_wait, waiting on a signal. Because of this, they don't see stop set to 1, and exit_handler (I'm assuming a Ctrl+c software signal?) is done signaling, so the remaining threads that lost the _broadcast competition are stuck in limbo, waiting on a signal that will never come, and unable to read that the stop flag has been set.
I think there are 2 options to work-around/fix this:
Use pthread_cond_timedwait. Even without being signaled, this will return from waiting at the specified time interval, see that stop == 1, and then exit.
Add pthread_cond_signal or pthread_cond_broadcast at the end of your worker function. This way, right before a thread exits, it will signal the cond variable allowing any other waiting threads to grab the mutex and finish processing. There is no harm in signaling a conditional variable if no threads are waiting on it, so this should be fine even for the last thread.
EDIT: Here is an MCVE that proves (as far as I can tell) that my answer above is wrong, heh. As soon as I press Ctrl+c, the program exits "immediately", which says to me all the threads are quickly acquiring the mutex after the broadcast, seeing that stop is false, and exiting. Then main joins on the threads and it's process over.
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <stdbool.h>
#include <signal.h>
#include <unistd.h>
#define NUM_THREADS 3
#define STACK_SIZE 10
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t c = PTHREAD_COND_INITIALIZER;
volatile bool stop = false;
int stack[STACK_SIZE] = { 0 };
int sp = 0; // stack pointer,, also doubles as the current stack size
void SigHandler(int sig)
{
if (sig == SIGINT)
{
stop = true;
}
else
{
printf("Received unexcepted signal %d\n", sig);
}
}
void* worker(void* param)
{
long tid = (long)(param);
while (stop == false)
{
// acquire the lock
pthread_mutex_lock(&m);
while (sp <= 0) // sp should never be < 0
{
// there is no data in the stack to consume, wait to get signaled
// this unlocks the mutex when it is called, and locks the
// mutex before it returns
pthread_cond_wait(&c, &m);
}
// when we get here we should be guaranteed sp >= 1
printf("thread %ld consuming stack[%d] = %d\n", tid, sp-1, stack[sp-1]);
sp--;
pthread_mutex_unlock(&m);
int sleepVal = rand() % 10;
printf("thread %ld sleeping for %d seconds...\n", tid, sleepVal);
sleep(sleepVal);
}
pthread_exit(NULL);
}
int main(void)
{
pthread_t threads[NUM_THREADS];
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
srand(time(NULL));
for (long i=0; i<NUM_THREADS; i++)
{
int rc = pthread_create(&threads[i], &attr, worker, (void*)i);
if (rc != 0)
{
fprintf(stderr, "Failed to create thread %ld\n", i);
}
}
while (stop == false)
{
// produce data in bursts
int numValsToInsert = rand() % (STACK_SIZE - sp);
printf("main producing %d values\n", numValsToInsert);
// acquire the lock
pthread_mutex_lock(&m);
for (int i=0; i<numValsToInsert; i++)
{
// produce values for the stack
int val = rand() % 10000;
// I think this should already be guaranteed..?
if (sp+1 < STACK_SIZE)
{
printf("main pushing stack[%d] = %d\n", sp, val);
stack[sp++] = val;
// signal the workers that data is ready
//printf("main signaling threads...\n");
//pthread_cond_signal(&c);
}
else
{
printf("stack full!\n");
}
}
pthread_mutex_unlock(&m);
// signal the workers that data is ready
printf("main signaling threads...\n");
pthread_cond_broadcast(&c);
int sleepVal = 1;//rand() % 5;
printf("main sleeping for %d seconds...\n", sleepVal);
sleep(sleepVal);
}
for (long i=0; i<NUM_THREADS; i++)
{
pthread_join(threads[i], NULL);
}
return 0;
}
I'm trying to simulate context switching through synchronization between threads (starting with 2, for now) such that each thread acts as a process and the process switching occurs at every TIMESLICE interval (3 ms, for now). The thread switching by executing only one thread at a time is running perfectly although, the issue comes when the first thread exits but the pthread_cond_signal that is executed pre-exit of the calling thread does not wake up the pthread_cond_wait of the other thread.
Here is the C program:
#include<stdio.h> //for printf
#include<pthread.h> //for threads, mutual exclusion, conditional wait and signalling
#include<time.h> //for accurate clock time
#include<limits.h> //for min-max limits of several data types
#include<inttypes.h> //for using uint64_t data type
#define bool int
#define true 1
#define false 0
#define TIMESLICE 3000000 //nanoseconds to define milliseconds
#define BILLION 1000000000L
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t lock_t = PTHREAD_MUTEX_INITIALIZER;
int numProcWait = 2;
void *threadFunction(void *arg) {
int i;
uint64_t diff=0;
bool locked,unlockCheck=true; // locked for synchronizing the thread withing itself
// unlock check for if the for loop exits without unlocking the mutex and without signalling the other thread
struct timespec start, end;
pthread_cond_wait(&cond,&lock_t);
pthread_mutex_lock(&lock);
clock_gettime(CLOCK_MONOTONIC, &start);
locked = true;
for (i = 0; i < INT_MAX/2048; i++) {
if(locked==false) {
pthread_mutex_lock(&lock);
printf("Lock acquired by Thread id: %lu\n",pthread_self());
locked = true;
diff = 0;
clock_gettime(CLOCK_MONOTONIC, &start);
}
clock_gettime(CLOCK_MONOTONIC, &end);
diff = BILLION * (end.tv_sec - start.tv_sec) + end.tv_nsec - start.tv_nsec;
unlockCheck = true;
if(diff > TIMESLICE) {
if(i==INT_MAX-1)
unlockCheck = false;
if(numProcWait>1) {
locked = false;
printf("Lock released by Thread id: %lu\n",pthread_self());
printf("Diff Time: %" PRIu64 " ms\n\n",diff/1000000);
pthread_mutex_unlock(&lock);
printf("Unlocking from thread Successful! Thread ID: %lu\n\n",pthread_self());
pthread_cond_signal(&cond);
printf("Waiting Thread id: %lu\n\n",pthread_self());
pthread_cond_wait(&cond,&lock_t);
printf("Received Signal Successful! Thread ID: %lu\n\n",pthread_self());
}
}
}
//this condition would occur false on a very rare case
if(unlockCheck) {
printf("Lock released from out of the loop by Thread id: %lu\n\n",pthread_self());
//this condition is executed but it doesn't wakes up the pthread_cond_wait function of the other thread.
pthread_mutex_unlock(&lock);
pthread_cond_signal(&cond);
}
printf("EXITING THREAD: %lu\n\n",pthread_self());
numProcWait--;
printf("Number of processes waiting: %d\n\n",numProcWait);
return NULL;
}
int main() {
pthread_t tid[2];
uint64_t diff;
struct timespec start, end;
clock_gettime(CLOCK_MONOTONIC, &start);
pthread_create(&tid[0], NULL, threadFunction, NULL);
pthread_create(&tid[1], NULL, threadFunction, NULL);
sleep(1);
pthread_cond_broadcast(&cond);
pthread_join(tid[0], NULL);
printf("Thread 1 EXITED. No. of Waiting Processes: %d\n",numProcWait);
//pthread_cond_signal(&cond);
pthread_join(tid[1], NULL);
printf("Thread 2 EXITED. No. of Waiting Processes: %d\n",numProcWait);
clock_gettime(CLOCK_MONOTONIC, &end);
diff = BILLION * (end.tv_sec - start.tv_sec) + end.tv_nsec - start.tv_nsec;
printf("Total Run Time: %" PRIu64 " ms\n",diff/1000000);
pthread_mutex_destroy(&lock);
pthread_cond_destroy(&cond);
}
You need to lock the mutex before calling condition wait and signal as below.
Wait:
pthread_mutex_lock(&lock_t);
pthread_cond_wait(&cond,&lock_t);
pthread_mutex_unlock(&lock_t);
Signal:
pthread_mutex_lock(&lock_t);
pthread_cond_signal(&cond);
pthread_mutex_unlock(&lock_t);
Off the top of my head: The code's problem seems to be that it uses two mutexes, lock and lock_t. The code locks lock and calls pthread_cond_wait with lock_t as arg.
A good idea may be to delete the lock_t mutex and only use one mutex.
you are using conditional-variables wrong
first of all, you need to acquire the lock before calling pthread_cond_wait()
but then your logic is wrong,
pthread_mutex_unlock(&lock);
printf("Unlocking from thread Successful! Thread ID: %lu\n\n",pthread_self());
pthread_cond_signal(&cond);
// you need to lock here, but its undefined which thread will
// acquire the lock first, so its possible, that the same thread
// will acquire the lock instantly again
// -> mutex are not fair!
pthread_mutex_lock(&lock_t);
printf("Waiting Thread id: %lu\n\n",pthread_self());
pthread_cond_wait(&cond,&lock_t);
printf("Received Signal Successful! Thread ID: %lu\n\n",pthread_self());
could either add a sleep (bad idea) or, set up a global variable, which indicates, which thread is currently running.
int current_thread=0;
and then you need to adjust every cond_wait to
current_thread = NEXT_THREAD;
pthread_mutex_unlock(&lock);
printf("Unlocking from thread Successful! Thread ID: %lu\n\n",pthread_self());
pthread_cond_signal(&cond);
pthread_mutex_lock(&lock_t);
while (current_thread != YOUR_LOCAL_THREAD_ID) {
pthread_cond_wait(&cond,&lock_t);
}
printf("Received Signal Successful! Thread ID: %lu\n\n",pthread_self());
My program performs "thread synchronization" using pthread_cond_wait and pthread_cond_signal. It seems to fail about one in 20 times.
I have a shared variable count which is initialized to 0.
One thread increments count until count is 20 , then signals a condition variable.
The code is below.
void* inc_count(void *parm)
{
int i;
for(i = 0 ; i <25; i ++)// adds count to 25
{
pthread_mutex_lock(&mutex1);
count ++;
printf("Thread %lld , count = %d\n",(long long int)pthread_self(),count);
if(count == 20)
{
pthread_cond_signal(&cond);
printf("Thread %lld sends a signal!\n",(long long int)pthread_self());
}
pthread_mutex_unlock(&mutex1);
}
pthread_exit(NULL);
}
void* watch_count(void *parm)
{
while(count < 20)
{
pthread_mutex_lock(&mutex1);
pthread_cond_wait(&cond,&mutex1);
printf("Thread %lld receives the signal!\n",(long long int)pthread_self());
}
pthread_mutex_unlock(&mutex1);
pthread_exit(NULL);
}
int main()
{
pthread_t pt1,pt2,pt3,pt4;
pthread_mutex_init(&mutex1,NULL);
pthread_mutex_init(&mutex2,NULL);
pthread_cond_init(&cond,NULL);
pthread_create(&pt1,NULL,inc_count,NULL);
pthread_create(&pt2,NULL,watch_count,NULL);
pthread_join(pt1,NULL);
pthread_join(pt2,NULL);
}
From the picture, you can see that thread pt2 didn't receive the signal, why?
Your watch_count() function has problems:
You're locking the mutex inside a while loop and only unlocking outside it (could try to lock more than once without unlocking)
You don't hold the mutex when checking count < 20
To fix it, you need lock the mutex before the loop instead of inside it:
pthread_mutex_lock(&mutex1);
while(count < 20)
{
pthread_cond_wait(&cond,&mutex1);
printf("Thread %lld receives the signal!\n",(long long int)pthread_self());
}
pthread_mutex_unlock(&mutex1);
That will keep you from locking the mutex more than once before unlocking, and ensure that count can't be modified and the signal can't be sent between the test in the while condition and the pthread_cond_wait (inc_count()'s change to count and sending the signal would have to occur either before the while loop, or during the pthread_cond_wait()).
Even with those changes, there's still the possibility that the wait won't happen at all if count is already 20 by the time watch_count() checks it, though.
#Dmitri's diagnosis at the end of his comment is the likely cultprit IMO: it's possible that your inc_count thread gets past 20 before the watch_count thread first checks the while condition.
Why use the while loop at all? When inc_count sends the signal, you already know that the count is over 20, so why check for it?
My solution is to do some handshaking and make sure that watch_count enters its wait condition before inc_count starts counting. To achieve this, you need to make sure that the following things happen in order:
inc_count enters its code first and then waits for watch_count to initialize
watch_count enters its code and signals inc_count that it's ready to go
watch_count waits for inc_count to signal it
inc_count starts counting and signals watch_count when it gets to 20
I achieved this by grabbing a mutex before starting either thread, then counting on the first thread to enter its "ready and waiting" condition before starting the second thread. When the second thread is ready it signals the first thread to go ahead and start counting, here's my code:
void* inc_count(void *parm)
{
// holding this mutex on entry;
pthread_mutex_t* m = parm;
int i;
// wait for the watch_count thread to be ready
pthread_cond_wait(&init_cond, m);
pthread_mutex_unlock(m);
// start counting
for(i = 0 ; i < 10000; i ++)
{
pthread_mutex_lock(&count_mutex);
count++;
printf("Thread %lld , count = %d\n",
(long long int) pthread_self(),
count);
if(count == 20)
{
pthread_cond_signal(&count_cond);
printf("Thread %lld sends a signal!\n",
(long long int)pthread_self());
}
pthread_mutex_unlock(&count_mutex);
}
pthread_exit(NULL);
}
void* watch_count(void *parm)
{
// holding this mutex on entry
pthread_mutex_t* m = parm;
pthread_cond_signal(&init_cond); // tell inc_count that you're ready
pthread_cond_wait(&count_cond,m); // wait for him to get to 20
pthread_mutex_unlock(m);
pthread_mutex_lock(&count_mutex);
printf("Thread %lld receives the signal! count == %d\n",
(long long int)pthread_self(),
count);
pthread_mutex_unlock(&count_mutex);
pthread_exit(NULL);
}
int main()
{
pthread_t pt1,pt2;
pthread_mutex_t init_mutex, count_mutex;
pthread_mutex_init(&init_mutex,NULL);
pthread_mutex_init(&count_mutex,NULL);
pthread_cond_init(&init_cond,NULL);
pthread_cond_init(&count_cond,NULL);
// this mutex is released when inc_count has initialized
pthread_mutex_lock(&init_mutex);
pthread_create(&pt1,NULL,inc_count,(void *) &init_mutex);
// this mutex is released when watch-count has initialized
pthread_mutex_lock(&init_mutex);
pthread_create(&pt2,NULL,watch_count,(void *) &init_mutex);
pthread_join(pt1,NULL);
pthread_join(pt2,NULL);
}