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

我正在研究一个项目,并尝试使用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()总是执行,因为它在关键部分?

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

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

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

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

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

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

 s1 s2 r1 r2 s3 s4 s5 

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

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

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

 #include  #include  #include  #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()之前测试某些内容,如果不这样做,并且之前已经调用过函数,那么您可能会永远睡不着觉。

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

 #include  #include  #include  #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$