使用pthread_cond_signal与pthread_cond_timedwait的线程通信超时问题
问题:双线程信号交互程序频繁触发超时终止
我编写了一个包含两个互相发送信号的线程的程序,初始运行符合预期,但多次运行后会因pthread_cond_timedwait触发超时而终止。测试中设置一旦某个线程触发超时就停止程序,期望程序能一直运行直到手动终止,请问该如何解决?
程序代码
#include <errno.h> #include <pthread.h> #include <signal.h> #include <stdbool.h> #include <stdint.h> #include <stdio.h> #include <sys/time.h> #include <time.h> #include <unistd.h> pthread_mutex_t mutex1; pthread_cond_t cond1; pthread_mutex_t mutex2; pthread_cond_t cond2; bool run = true; void signal_handler(int sig_num) { run = false; } void* thread1(void* arg) { struct timespec ts; struct timeval now; uint32_t runs = 0; while (run) { pthread_mutex_lock(&mutex1); gettimeofday(&now, NULL); ts.tv_sec = now.tv_sec + 1; ts.tv_nsec = now.tv_usec * 1000; pthread_mutex_unlock(&mutex1); pthread_mutex_lock(&mutex2); int result = pthread_cond_timedwait(&cond2, &mutex2, &ts); if (result == ETIMEDOUT) { printf("Thread 1: Wait timed out\n"); run = false; } else if (result == 0) { // printf("Thread 1: Signal received from Thread 2\n"); if (pthread_cond_signal(&cond1) != 0) { printf("Failed to signal cond1\n"); } runs++; } pthread_mutex_unlock(&mutex2); } printf("T1: runs=%d\n", runs); return NULL; } void* thread2(void* arg) { struct timespec ts; struct timeval now; uint32_t runs = 0; while (run) { pthread_mutex_lock(&mutex2); gettimeofday(&now, NULL); ts.tv_sec = now.tv_sec + 1; ts.tv_nsec = now.tv_usec * 1000; // printf("Thread 2: Sending signal to Thread 1\n"); if (pthread_cond_signal(&cond2) != 0) { printf("Failed to signal cond2\n"); } pthread_mutex_unlock(&mutex2); pthread_mutex_lock(&mutex1); int result = pthread_cond_timedwait(&cond1, &mutex1, &ts); if (result == ETIMEDOUT) { printf("Thread 2: Wait timed out\n"); run = false; } else { runs++; } pthread_mutex_unlock(&mutex1); } printf("T2: runs=%d\n", runs); return NULL; } int main() { pthread_t tid1, tid2; signal(SIGINT, signal_handler); signal(SIGTERM, signal_handler); pthread_mutex_init(&mutex1, NULL); pthread_cond_init(&cond1, NULL); pthread_mutex_init(&mutex2, NULL); pthread_cond_init(&cond2, NULL); pthread_create(&tid1, NULL, thread1, NULL); pthread_create(&tid2, NULL, thread2, NULL); pthread_join(tid1, NULL); pthread_join(tid2, NULL); pthread_mutex_destroy(&mutex1); pthread_cond_destroy(&cond1); pthread_mutex_destroy(&mutex2); pthread_cond_destroy(&cond2); return 0; }
运行输出示例
$ /tmp/test Thread 2: Wait timed out T2: runs=84498 Thread 1: Wait timed out T1: runs=84499 $ /tmp/test Thread 2: Wait timed out T2: runs=148 Thread 1: Wait timed out T1: runs=148
问题原因与解决办法
核心问题
- 超时时间提前计算导致过期:超时时间在循环早期就计算好了,但线程切换、锁竞争会消耗时间,等实际调用
pthread_cond_timedwait时,这个1秒的超时窗口可能已经过了,直接触发超时。 - 条件变量信号丢失:线程2先发送
cond2信号,再等待cond1,但此时线程1可能还没进入cond2的等待状态,信号直接被丢弃。后续线程1进入等待时,没有信号触发,最终超时。 - 未遵循条件变量的正确使用范式:条件变量应该和共享状态标记配合使用,单独发送信号很容易出现丢信号的情况。
修复后的代码
#include <errno.h> #include <pthread.h> #include <signal.h> #include <stdbool.h> #include <stdint.h> #include <stdio.h> #include <sys/time.h> #include <time.h> #include <unistd.h> pthread_mutex_t mutex1; pthread_cond_t cond1; bool cond1_signaled = false; // 配合cond1的状态标记 pthread_mutex_t mutex2; pthread_cond_t cond2; bool cond2_signaled = false; // 配合cond2的状态标记 bool run = true; void signal_handler(int sig_num) { run = false; } void* thread1(void* arg) { struct timespec ts; struct timeval now; uint32_t runs = 0; while (run) { pthread_mutex_lock(&mutex2); // 先检查是否已有信号,避免丢信号 while (!cond2_signaled && run) { // 等待前才计算超时时间,确保准确性 gettimeofday(&now, NULL); ts.tv_sec = now.tv_sec + 1; ts.tv_nsec = now.tv_usec * 1000; int result = pthread_cond_timedwait(&cond2, &mutex2, &ts); if (result == ETIMEDOUT) { printf("Thread 1: Wait timed out\n"); run = false; break; } // 虚假唤醒时重新检查状态 } if (cond2_signaled && run) { cond2_signaled = false; // 重置状态 // 发送信号给线程2 pthread_mutex_lock(&mutex1); cond1_signaled = true; pthread_cond_signal(&cond1); pthread_mutex_unlock(&mutex1); runs++; } pthread_mutex_unlock(&mutex2); } printf("T1: runs=%d\n", runs); return NULL; } void* thread2(void* arg) { struct timespec ts; struct timeval now; uint32_t runs = 0; while (run) { // 发送信号给线程1 pthread_mutex_lock(&mutex2); cond2_signaled = true; pthread_cond_signal(&cond2); pthread_mutex_unlock(&mutex2); pthread_mutex_lock(&mutex1); // 检查状态,等待信号 while (!cond1_signaled && run) { gettimeofday(&now, NULL); ts.tv_sec = now.tv_sec + 1; ts.tv_nsec = now.tv_usec * 1000; int result = pthread_cond_timedwait(&cond1, &mutex1, &ts); if (result == ETIMEDOUT) { printf("Thread 2: Wait timed out\n"); run = false; break; } } if (cond1_signaled && run) { cond1_signaled = false; runs++; } pthread_mutex_unlock(&mutex1); } printf("T2: runs=%d\n", runs); return NULL; } int main() { pthread_t tid1, tid2; signal(SIGINT, signal_handler); signal(SIGTERM, signal_handler); pthread_mutex_init(&mutex1, NULL); pthread_cond_init(&cond1, NULL); pthread_mutex_init(&mutex2, NULL); pthread_cond_init(&cond2, NULL); pthread_create(&tid1, NULL, thread1, NULL); pthread_create(&tid2, NULL, thread2, NULL); pthread_join(tid1, NULL); pthread_join(tid2, NULL); pthread_mutex_destroy(&mutex1); pthread_cond_destroy(&cond1); pthread_mutex_destroy(&mutex2); pthread_cond_destroy(&cond2); return 0; }
关键改动说明
- 增加状态标记:为每个条件变量添加
condX_signaled布尔值,用来记录是否有未处理的信号,彻底解决信号丢失问题。 - 延迟计算超时时间:把
gettimeofday移到pthread_cond_timedwait调用的前一刻,确保超时时间是当前时间+1秒,避免提前计算导致的超时过期。 - 使用while循环等待:处理条件变量的虚假唤醒问题,每次被唤醒后重新检查状态标记,确保是有效信号触发的唤醒。
- 原子更新状态:修改状态标记时必须持有对应的互斥锁,保证线程间的状态可见性。
这样修改后,程序可以稳定运行直到手动用Ctrl+C终止,不会再出现无故超时的情况。
内容的提问来源于stack exchange,提问作者paperwork
相关产品推荐
相关产品推荐

