Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
175 views
in Technique[技术] by (71.8m points)

c - Lost wakeups in pthreads

I've written a little to program to try out pthread conditional waits. But the problem is that there is no guarantee that a signal when sent out will be caught, thereby the thread losing the wakeup. How do I get around this?

#include<stdio.h>
#include<pthread.h>

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

void *thread_func1(void* arg){
  printf("thread1 started
");
  pthread_mutex_lock(&mutex);
  printf("thread1: signalling
");
  pthread_cond_signal(&cond);
  printf("thread1: signalled
");
  pthread_mutex_unlock(&mutex);
  printf("thread1: exiting
");
  pthread_exit(0);
}

void *thread_func2(void* arg){
  printf("thread2 started
");
  pthread_mutex_lock(&mutex);
  printf("thread2: waiting for signal..
");
  pthread_cond_wait(&cond, &mutex);
  printf("thread2: signal received
");
  pthread_mutex_unlock(&mutex);
  printf("thread2: exiting
");
  pthread_exit(0);
}

int main(int argc, char** argv){
  pthread_t thread1, thread2;

  pthread_create(&thread1, NULL, thread_func1, NULL);
  pthread_create(&thread2, NULL, thread_func2, NULL);

  pthread_join(thread1, 0);
  pthread_join(thread2, 0);

  return 0;
}

Here is an output from a run:

thread1 started
thread1: signalling
thread2 started
thread2: waiting for signal..
thread1: signalled
thread1: exiting
// nothing happens now; where is the signal??

Here's from another one (which works):

thread2 started
thread2: waiting for signal..
thread1 started
thread1: signalling
thread1: signalled
thread1: exiting
thread2: signal received
thread2: exiting
// program successfully exits

I'm not concerned about any kind of critical section for now, so I haven't used any locks.

How do I ensure this thing works for each run?

Edit: I have edited the code as per alk's answer below. I have added the initializers and locks. The original code I posted is here.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

As you have noticed, thread 1 might signal the condition variable before thread 2 calls pthread_cond_wait(). The condition variable does not "remember" that it has been signaled, so the wakeup will be lost. Therefore, you need to use some kind of variable to determine whether thread 2 needs to wait.

int signalled = 0;

void *thread_func1(void* arg){
  printf("thread1 started
");
  pthread_mutex_lock(&mutex);
  printf("thread1: signalling
");
  signalled = 1;
  pthread_cond_signal(&cond);
  printf("thread1: signalled
");
  pthread_mutex_unlock(&mutex);
  printf("thread1: exiting
");
  pthread_exit(0);
}

void *thread_func2(void* arg){
  printf("thread2 started
");
  pthread_mutex_lock(&mutex);
  printf("thread2: waiting for signal..
");
  if(!signalled) {
    pthread_cond_wait(&cond, &mutex);
  }
  printf("thread2: signal received
");
  pthread_mutex_unlock(&mutex);
  printf("thread2: exiting
");
  pthread_exit(0);
}

However, this code is still not correct. The pthreads spec states that "spurious wakeups" may occur on condition variables. This means that pthread_cond_wait() might return even if nobody has called pthread_cond_signal() or pthread_cond_broadcast(). Therefore, you need to check the flag in a loop, rather than just once:

void *thread_func2(void* arg){
  printf("thread2 started
");
  pthread_mutex_lock(&mutex);
  printf("thread2: waiting for signal..
");
  while(!signalled) {
    pthread_cond_wait(&cond, &mutex);
  }
  printf("thread2: signal received
");
  pthread_mutex_unlock(&mutex);
  printf("thread2: exiting
");
  pthread_exit(0);
}

Update: An alternate method to combine the function of a condition variable and a counter is to use a semaphore.

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
sem_t sem;

void *thread_func1(void* arg){
  printf("thread1 started
");
  printf("thread1: signalling
");
  sem_post(&sem);
  printf("thread1: signalled
");
  printf("thread1: exiting
");
  pthread_exit(0);
}

void *thread_func2(void* arg){
  printf("thread2 started
");
  printf("thread2: waiting for signal..
");
  sem_wait(&sem);
  printf("thread2: signal received
");
  printf("thread2: exiting
");
  pthread_exit(0);
}

int main(int argc, char** argv){
  pthread_t thread1, thread2;

  sem_init(&sem);

  pthread_create(&thread1, NULL, thread_func1, NULL);
  pthread_create(&thread2, NULL, thread_func2, NULL);

  pthread_join(thread1, 0);
  pthread_join(thread2, 0);

  sem_destroy(&sem);

  return 0;
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...