pthread_cond_wait被唤醒后是否立即获取互斥锁?如何保障线程B优先获取?
首先得纠正你代码里的一个致命bug:while(condition = 0)是赋值操作,不是判断!这会把condition直接设为0,循环条件永远为假,线程B根本不会进入pthread_cond_wait等待。正确写法应该是while(condition == 0),这个一定要先改过来,不然逻辑完全走不通。
接下来回到你的核心问题:
1. pthread_cond_wait被信号唤醒后会立即获取互斥锁吗?
答案是不会。当pthread_cond_wait被信号(或广播)唤醒后,它并不会直接拿到互斥锁,而是会加入到这个互斥锁的等待队列中,和其他正在尝试获取该锁的线程(比如你的线程A)一起竞争锁资源。只有当它竞争到锁之后,pthread_cond_wait才会返回,线程B才能继续执行后续代码。
本质上,唤醒只是让线程B从条件等待队列转移到互斥锁的等待队列,能不能拿到锁还是要看竞争结果。
2. 现有代码中线程A会不会先于B重新获取互斥锁?
肯定会有这种情况!你的线程A在发送信号后立刻解锁,然后下一轮循环马上又调用pthread_mutex_lock。而线程B被唤醒后才开始尝试获取锁,线程A的解锁和重新加锁动作几乎是连续的,在调度上大概率比线程B更快抢到锁,导致线程B即使被唤醒了,也会因为拿不到锁而继续等待,无法及时执行do_something_else()。
3. 如何确保B被唤醒后能获取互斥锁?
要实现这个需求,你需要让线程A在唤醒B之后,不要立刻去抢锁,而是等待线程B处理完任务后再继续循环。可以通过新增一个反向的条件变量来实现“双向通知”:
修改后的代码示例
#include <pthread.h> pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; // 用于A唤醒B的条件变量 pthread_cond_t cond_b = PTHREAD_COND_INITIALIZER; // 用于B唤醒A的条件变量 pthread_cond_t cond_a = PTHREAD_COND_INITIALIZER; int condition_b = 0; // A通知B的条件 int condition_a = 0; // B通知A的条件 // 线程A的函数 void* func_A(void *arg){ while(1) { pthread_mutex_lock(&lock); do_something(); // 设置条件,唤醒B condition_b = 1; pthread_cond_signal(&cond_b); // 等待B处理完成的信号 while(condition_a == 0) { pthread_cond_wait(&cond_a, &lock); } // 重置A的等待条件 condition_a = 0; pthread_mutex_unlock(&lock); } return NULL; } // 线程B的函数 void* func_B(void *arg) { while(1) { pthread_mutex_lock(&lock); // 等待A的唤醒信号 while(condition_b == 0) { pthread_cond_wait(&cond_b, &lock); } do_something_else(); // 重置B的条件,唤醒A继续循环 condition_b = 0; condition_a = 1; pthread_cond_signal(&cond_a); pthread_mutex_unlock(&lock); } return NULL; }
逻辑说明
- 线程A完成任务后,设置
condition_b并唤醒B,然后通过pthread_cond_wait等待B的通知,此时A会释放互斥锁,这样线程B就能顺利获取锁并处理任务。 - 线程B处理完成后,重置条件并唤醒A,此时A才会重新获取锁,进入下一轮循环。
- 这种双向通知的方式,彻底避免了A和B的锁竞争,确保B被唤醒后一定能拿到锁执行任务。
另外还有一种更简单的场景:如果线程A不需要立刻循环执行,而是可以在唤醒B后暂停一下,也能降低A抢锁的概率,但这种方式依赖系统调度,不能保证100%可靠,所以更推荐上面的双向通知方案。
内容的提问来源于stack exchange,提问作者eesweng

