Problem with cond.signal or lock.release?
Hi when I run the following code, I find that the signaling thread continues to run for a long time before the other thread starts... why is that? isn't the woken up thread supposed to run as soon as the signaler releases the lock?. Or does the OS take a long time to put the sleeping thread back on the ready queue?.
#include pthread.h
#include stdio.h
#include stdlib.h
void stupidfunction1(void *arg);
void stupidfunction2(void *arg);
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond1 = PTHREAD_COND_INITIALIZER;
int thread1count,thread2count;
int thread1waiting = 0;
int thread2waiting = 0;
void main()
{
printf("Hello World\n");
pthread_t thread1,thread2;
int i;
thread1count = 0;
thread2count = 0;
i = pthread_create(&thread1,NULL,&stupidfunction1,NULL);
i = pthread_create(&thread2,NULL,&stupidfunction2,NULL);
pthread_join(thread1,NULL);
pthread_join(thread2,NULL);
printf("Done with everythinh");
}
void stupidfunction1(void *arg)
{
int i = 0;
for(i = 0;i<50;i++)
{
thread1count++;
pthread_mutex_lock(&mutex1);
if((thread1count-thread2count)>5)
{
thread1waiting = 1;
printf("thread1 waiting \n");
pthread_cond_wait(&cond1,&mutex1);
thread1waiting = 0;
}
else if((thread2waiting == 1) && abs(thread1count-thread2count)<1)
{
pri开发者_StackOverflow中文版ntf("signalling thread2\n");
pthread_cond_signal(&cond1);
}
pthread_mutex_unlock(&mutex1);
printf("Hey its thread 1 @ %d\n",thread1count);
}
}
void stupidfunction2(void *arg)
{
int i = 0;
for(i = 0;i<50;i++)
{
thread2count++;
pthread_mutex_lock(&mutex1);
if((thread2count-thread1count)>5)
{
thread2waiting = 1;
printf("thread2 waiting \n");
pthread_cond_wait(&cond1,&mutex1);
thread2waiting = 0;
}
else if((thread1waiting == 1) && abs(thread1count-thread2count)<1)
{
printf("signalling thread1\n");
pthread_cond_signal(&cond1);
}
pthread_mutex_unlock(&mutex1);
printf("Hey its thread 2 @ %d\n",thread2count);
}
}
OUTPUT:
Hey its thread 2 @ 1
Hey its thread 2 @ 2
Hey its thread 2 @ 3
Hey its thread 2 @ 4
Hey its thread 2 @ 5
thread2 waiting
Hey its thread 1 @ 1
Hey its thread 1 @ 2
Hey its thread 1 @ 3
Hey its thread 1 @ 4
Hey its thread 1 @ 5
signalling thread2
Hey its thread 1 @ 6
Hey its thread 1 @ 7
Hey its thread 1 @ 8
Hey its thread 1 @ 9
Hey its thread 1 @ 10
Hey its thread 1 @ 11
In direct answer to your question: no, pthread_mutex_unlock
and pthread_cond_signal
do not wake any waiting thread immediately. Instead, they may well just mark it as "ready to run", and then the OS will schedule the woken thread when it feels like it. Of course, the OS may decide to switch to that thread immediately (especially if it is higher priority than any currently executing thread), but it may not.
However, your code may not work correctly as written, anyway: you may have both threads running at the same time!
Just because pthread_cond_wait
returns, it does not mean that the condition variable has been signalled. This is called a "spurious wake". To use pthread_cond_wait
correctly, you must put it in a loop, where the condition associated with waking is tested whilst the mutex is held, immediately before calling pthread_cond_wait
. e.g.
void wait_until_signalled(int* wake_flag,pthread_cond_t* cond,pthread_mutex_t* mutex)
{
pthread_mutex_lock(&mutex);
while(!(*wake_flag)) /* if the int pointed to by wake_flag is non-zero then wake up */
{
pthread_cond_wait(&cond,&mutex);
}
pthread_mutex_unlock(&mutex);
}
void signal(int* wake_flag,pthread_cond_it* cond,pthread_mutex_t* mutex)
{
pthread_mutex_lock(&mutex);
*wake_flag=1; /* tell the waiting thread that it should wake */
pthread_mutex_unlock(&mutex);
pthread_cond_signal(&cond); /* wake up the thread if it is blocked in pthread_cond_wait*/
}
Of course, you probably want to check the return values of the pthread_xxx
calls.
Since the value pointed to by wake_flag
is only checked and modified with the mutex locked, then the waiting thread will definitely wake if it is set, and not return from wait_until_signalled
until the flag has been set. The call to pthread_cond_wait
atomically marks the thread as waiting, and unlocks the mutex, so either the call to pthread_cond_signal
will see the thread is waiting, and wake it so it can check the flag (which is already set), or the thread is not waiting, which means it must have locked the mutex AFTER the thread in signal
set the flag, in which case the waiting thread will see the flag set, and return.
Threads run when the scheduler gets around to running them.
Way one thread 2 doenst run quickly: When thread 1 signals thread 2 to wake up (and in your case many times before thread 2 actually does) all the threads waiting on the condition variable have to fight for the lock when the scheduler runs them. In your example, I bet the scheduler never gets to thread 2 before thread 1 finishes. A good bet is that your timeslice is 1ms.
Way two thread 2 doesnt run quickly: thread 1 signals thread 2, but when thread 2 wakes up thread 1 has the lock still (either because thread 1 is in a later loop or because thread2 woke immediately and thread 1 has the mutex locked before thread 1 called cond_signal), so thread 2 goes back to sleep as a waiter on the mutex now.
Still assuming for sake of example that thread 2 is the waiting thread.
Assuming you want thread 2 to wake up immediately...AFAIK there is nothing you can do.
If you realllllly wanted the thread 1 to STOP after signaling and you wanted to still use pthread_cond (there are other faster ways!), you could unlock the mutex before signaling (this allowed i think) and then call sched_yield() or nanosleep({0,0}) right after the signal. Bad for performance because the thread 1 just put itself on the back of the line of threads (with same priority) waiting to run. This will increase your ratio of context switches to run-time. This is also bad because of extra unlock/lock you have to do. On top of all this, thread 1 might wake up again before thread 2! You'd have to use a loop, keep signaling and yielding till that darn thread 2 does its work! Klunky!
By the way, my interpretation of volatile is that it tells the compiler that the variable is expected to change at any time. That variable would be a bad one to copy into a register at the start of the function for performance reasons. The compiler is pretty good about knowing which variables can do this, but from time to time it makes a mistake. I would say that when optimizations are turned on volatile could have an effect if gcc cached any shared variables in registers. I don't know if gcc caches globals in registers.
cheers.
I can see two mistakes here:
You are not locking the mutex around pthread_cond_signal. This means that the signal can be sent at a time where the other thread is between the check whether it should continue to run and the point where it goes into pthread_cond_wait, so the signal may get lost here.
The shared variables should be volatile. The compiler might decide to move the accesses outside of the loop, or partially unroll the loop and omit the access between two instances of the loop body.
Neither of these directly explain the symptom you are seeing; however the given behaviour is absolutely within the allowed range of the standard.
精彩评论