Pthread条件信号 - 未按预期工作

时间:2009-03-22 15:55:53

标签: c synchronization pthreads

我正在开发一个项目并尝试使用pthread_cond_wait()pthread_cond_signal()来同步两个线程。

我的代码看起来像这样:

pthread_mutex_t lock_it = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t write_it = PTHREAD_COND_INITIALIZER;

   int main(int argc, char**argv)
  {

     pthread_t t_send_segments, t_recv_acks;

     pthread_create(&t_send_segments, NULL, send_segments, (void*)NULL);
     pthread_create(&t_recv_acks,     NULL, recv_acks,     (void*)NULL);

     pthread_join(t_recv_acks, (void**)NULL);

     pthread_mutex_destroy(&lock_it);
     pthread_cond_destroy(&write_it);
}

 void* send_segments(void *v) {
    for(;;) {
       pthread_mutex_lock(&lock_it);
       printf("s1\n");
       printf("s2\n");
       pthread_cond_wait(&write_it, &lock_it);
       printf("s3\n");
       printf("s4\n");
       printf("s5\n"); 
       pthread_mutex_unlock(&lock_it);
    }
    return 0;
 }

 void* recv_acks(void *v) {
    for(;;) {
       pthread_mutex_lock(&lock_it);
       printf("r1\n");
       pthread_cond_signal(&write_it);
       printf("r2\n");
       pthread_mutex_unlock(&lock_it);
    }  
    return 0;
 }

预期输出为:

s1
s2
r1
s3
s4
s5
s1
s2
r2
r1
s3
s4
s5

(etc)

我的输出完全没有遵循这种模式。显然我在某个地方有一个逻辑错误,但我不知道在哪里。为什么recv_acks()线程总是在它到达pthread_cond_signal()时不会产生 - 因为pthread_cond_wait()总是先执行(因为我创建的顺序)线程)和cond_wait()总是执行,因为它在临界区?

4 个答案:

答案 0 :(得分:8)

pthread_cond_signal函数不会导致当前线程产生并且不会释放互斥锁。它只是重新启动一个通过pthread_cond_wait在条件上暂停自己的线程。这只是意味着唤醒的线程可用于调度,它不会导致它立即执行。线程调度程序将在未来的某个时间安排它。

另外,仅仅因为s-thread被唤醒并且正在争夺互斥锁,这并不意味着它接下来会获得互斥锁。互斥体对于请求它的所有线程不一定公平。根据{{​​1}}手册页:“pthread_mutex锁定给定的互斥锁。如果互斥锁当前已解锁,则它将被锁定并由调用线程拥有,并且pthread_mutex_lock立即返回。”因此,r-thread可以在其循环中旋转几次,在调度程序换出之前,可以快乐地解锁并重新锁定互斥锁几次。这意味着如果调度程序在释放互斥锁的短暂时间内中断了r-thread,则s-thread只会获得互斥锁的机会。

要实现所需的输出,两个线程都需要在有条件的情况下控制它们的执行,并在挂起之前互相发出信号。但是,这可能是您真正想要对您的真实项目做的事情,也可能不是。

另一个注意事项:创建线程的顺序并不重要。创建线程不会产生创建线程。因此,主线程可能会在调度之前创建两个线程,并且线程调度程序可以自由地安排其中一个线程执行。如果s-thread确实在您的平台上首先运行,那恰好就是您平台上的实现行为,而不是应该依赖的东西。

答案 1 :(得分:6)

我似乎记得在某处pthread_cond_signal()实际上并没有导致线程立即产生。由于pthread_cond_signal()不释放锁,因此调用它的线程必须继续执行,直到它释放锁,并且只有然后才会产生并允许发出信号的线程从pthread_cond_wait()

如果是这种情况,那么我认为您的输出看起来像

s1
s2
r1
r2
s3
s4
s5

等......如果不是这样,您可能需要在问题中编辑实际输出以获得准确的答案。

答案 2 :(得分:4)

您正在错误地使用条件。我不知道你想要什么,但你想要做的事情看起来很像一个典型的同步问题,称为生产者/消费者问题,可以用我之后的条件来实现。

您应该看看这个,以了解如何使用条件。 问题如下: 我有两个线程,一个将数据写入固定大小的缓冲区,另一个从缓冲区读取数据。如果缓冲区已满,则第一个线程无法写入更多数据;如果缓冲区为空,则第二个线程无法读取。

#include <stdlib.h>
#include <stdio.h>

#include <pthread.h>

#define BUFFER_SIZE 4 

int buffer_nb_entries = 0; 

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 
pthread_cond_t cond = PTHREAD_COND_INITIALIZER; 

void send(){
for(;;){
  pthread_mutex_lock(&mutex);
  while( buffer_nb_entries == BUFFER_SIZE) /* If buffer is full, then wait */
    pthread_cond_wait(&cond, &mutex); 

  /* Here I am sure that buffer is not full */
  printf("sending\n"); 
  buffer_nb_entries++;

  pthread_cond_signal(&cond); // signal that the condition has changed. 
  pthread_mutex_unlock(&mutex); 
 }
}

void receive(){
  for(;;){
    pthread_mutex_lock(&mutex); 
    while(buffer_nb_entries == 0)
      pthread_cond_wait(&cond, &mutex);
    /* Here I am sure that buffer is not empty */
    printf("receiving\n");
    buffer_nb_entries--; 
    pthread_cond_signal(&cond); 
    pthread_mutex_unlock(&mutex); 
  }

}

int main(){
  pthread_t s, r; 

  pthread_create(&s, NULL, (void *(*)(void*))send, NULL); 
  pthread_create(&r, NULL, (void *(*)(void*))receive, NULL); 

  pthread_join(s, NULL); 

}

请注意,在调用pthread_cond_wait()之前必须先测试一下,如果不这样做,如果之前调用过函数,那么你可能会永远睡不着觉。

答案 3 :(得分:1)

我认为你的麻烦来自于尝试使用一把锁来解决太多问题。你应该只使用锁定一件事,这样就不会混淆你在等什么。我建议为写信号添加第二个锁。此外,您应该为第二组消息添加第二个cond_wait。如果不这样做,那么运行的顺序将是随机的。这是我编辑的程序版本:

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

#define NUM_MESSAGES 3
int messages = 0;
void * send_segments(void *);
void * recv_acks(void *v);
pthread_mutex_t lock_it = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t write_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t write_cond = PTHREAD_COND_INITIALIZER;

   int main(int argc, char**argv)
  {

     pthread_t t_send_segments, t_recv_acks;

     pthread_create(&t_send_segments, NULL, send_segments, (void*)NULL);
     pthread_create(&t_recv_acks,     NULL, recv_acks,     (void*)NULL);

     pthread_join(t_recv_acks, (void**)NULL);
     pthread_join(t_send_segments, (void**)NULL);

     //pthread_mutex_destroy(&lock_it);
     //pthread_cond_destroy(&write_it);
}

 void* send_segments(void *v) {
    for(;;) {
       pthread_mutex_lock(&lock_it);
       printf("s1\n");
       printf("s2\n");
       pthread_mutex_unlock(&lock_it);

       // wait for other thread
       pthread_mutex_lock(&write_mutex);
       pthread_cond_wait(&write_cond, &write_mutex);
       pthread_mutex_unlock(&write_mutex);

       pthread_mutex_lock(&lock_it);
       printf("s3\n");
       printf("s4\n");
       printf("s5\n"); 
       pthread_mutex_unlock(&lock_it);

       // wait for other thread
       pthread_mutex_lock(&write_mutex);
       pthread_cond_wait(&write_cond, &write_mutex);
       pthread_mutex_unlock(&write_mutex);

       if (messages > NUM_MESSAGES) return( NULL );
    }
    return 0;
 }


 void* recv_acks(void *v) {
    for(;;) {
       // write first response
       pthread_mutex_lock(&lock_it);
       printf("r1\n");
       pthread_mutex_unlock(&lock_it);

       // signal other thread
       pthread_mutex_lock(&write_mutex);
       pthread_cond_signal(&write_cond);
       pthread_mutex_unlock(&write_mutex);

       // write second response
       pthread_mutex_lock(&lock_it);
       printf("r2\n\n");
       // increment count before releasing lock, otherwise the other thread
       // will be stuck waiting for a write_cond signal
       messages++;
       pthread_mutex_unlock(&lock_it);

       // signal other thread
       pthread_mutex_lock(&write_mutex);
       pthread_cond_signal(&write_cond);
       pthread_mutex_unlock(&write_mutex);

       if (messages > NUM_MESSAGES) return(NULL);
    } 
    return 0;
 }

如果我运行此程序,我会得到以下输出(请注意,为了清晰起见,我在r2之后添加了第二个换行符):

leif@indurain:~/tmp$ ./test
s1
s2
r1
s3
s4
s5
r2

s1
s2
r1
s3
s4
s5
r2

s1
s2
r1
s3
s4
s5
r2

s1
s2
r1
s3
s4
s5
r2

leif@indurain:~/tmp$