条件变量(condition variable)也是线程同步的一种工具,它可以让线程暂停执行并释放处理器,直到某个条件满足后被重新唤醒而继续执行。条件变量必须与互斥量配合使用,以避免出现竟态条件。

POSIX下条件变量的数据类型位pthread_cond_t,与其相关的定义及函数如下:
#include <pthread.h>
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,const struct timespec *abstime);
int pthread_cond_destroy(pthread_cond_t *cond);
1、条件变量的初始化
与互斥量类似,条件变量也有静态初始化和动态初始化两种方式。
动态初始化函数:
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr)
参数:
①cond:指向条件变量结构体的指针;
②cond_atrr:指向条件变量结构体属性的指针;
当cond_atrr的值为NULL时,使用默认属性。
返回值:成功返回0,失败返回错误码。
错误码:
①EAGAIN:系统缺少除内存外的必要资源进行初始化;
②ENOMEM:没有足够的内存进行初始化
静态初始化:
使用常量PTHREAD_COND_INITIALIZER进行静态初始化。
例如:
pthread_cond_t my_cond = PTHREAD_COND_INITIALIZER;
2、唤醒条件变量
2.1、int pthread_cond_signal(pthread_cond_t *cond);
参数:要唤醒的条件变量的指针
返回值:成功返回0,失败返回错误码。
该函数可以从等待队列中唤醒一个等待某条件变量的线程。如果没有任何线程在等待该条件变量,则什么都不会发生。如果有多个线程在等待该条件变量,则仅会唤醒其中一个。
2.2、int pthread_cond_broadcast(pthread_cond_t *cond);
参数:要唤醒的条件变量的指针
返回值:成功返回0,失败返回错误码。
该函数可以唤醒所有等待某个条件变量的线程,如果没有任何线程等待该条件变量,则什么都不会发生。
条件变量的唤醒应配合唤醒标志及互斥量,与条件变量的等待息息相关。
3、条件变量的等待
3.1、无条件等待
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
参数:
①cond:指向条件变量的指针;
②mutex:持有的互斥量的指针;
返回值:成功返回0,失败返回错误码
错误码:
①EINVAL:参数值无效(cond或mutex);
②EINVAL:对于同一条件变量提供了不同的互斥锁;
③EPERM:互斥量没有被当前线程拥有;
该函数可以将线程挂起,并将其置于等待条件变量cond的队列中。
例如:
pthread_mutex_t my_mutex = PTHREAD_MUTEX_INITIALIZER;//互斥量
pthread_cond_t my_cond = PTHREAD_COND_INITIALIZER;//条件变量
int count=0;//条件
count++;
pthread_mutex_lock(&my_mutex);
while (count == BUFFER_SIZE) {
pthread_cond_wait(&my_cond, &my_mutex);
}
//进行其它操作
pthread_mutex_unlock(&my_mutex);
说明:
首先使用pthread_mutex_lock获取互斥量,然后进入while循环。判断while循环的条件,如果条件满足,即数量等于缓存区的容量,则执行pthread_cond_wait。若条件变量my_cond没有被通知,则将当前线程挂起,进入等待该条件变量(本例程中的my_cond)的队列,然后释放互斥量my_mutex。
pthread_cond_wait函数被唤醒后,会尝试获取互斥量my_mutex,若成功获取互斥量后,函数会返回。此时仍在while循环中,要判断while循环的条件。若条件不满足,则退出循环,继续执行后面的代码。最终应释放持有的互斥量。
POSIX允许pthread_cond_wait函数在条件变量没有满足的情况下偶尔返回(其值为0),这种情况被称为“虚假唤醒”。为了防止虚假唤醒,pthread_cond_wait应放入while循环中,配合标志位进行判断。当虚假唤醒后,由于while循环的条件依然满足,则再次执行pthread_cond_wait函数进入等待队列。
当条件变量没有满足,pthread_cond_wait函数会一直等待下去。
如果希望执行有时间限制的等待,则应调用pthread_cond_timedwait函数。
3.2、定时等待
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,const struct timespec *abstime);
参数:
①cond:指向条件变量的指针;
②mutex:指向互斥量的指针;
③abstime:指向timespec时间结构体的指针,表示超时的时间(绝对时间:从1970-1-1 00:00:00开始);
返回值:成功返回0,失败返回非零错误码(除了pthread_cond_wait()函数的返回值外,还包括:ETIMEDOUT超时)
timespec时间结构体定义如下:
struct timespec {
time_t tv_sec; // 秒(从 Epoch 1970-01-01 00:00:00 UTC 开始)
long tv_nsec; // 纳秒 [0, 999999999]
};
例如:
struct timespec ts; //定义结构体变量
clock_gettime(CLOCK_REALTIME, &ts); // 获取当前绝对时间
ts.tv_sec += 5; // 等待 5 秒
clock_gettime 用于获取指定时钟的当前时间,结果填充到timespec结构中。它是POSIX标准接口,常用于需要高精度时间戳或计算绝对超时时间的场景(如配合 pthread_cond_timedwait)。
例如:
pthread_mutex_lock(&my_mutex);
while (count == BUFFER_SIZE) {
result=pthread_cond_timedwait(&my_cond, &my_mutex,&ts);
if(0!=result)
{
//修改标识变量(可选)
pthread_mutex_unlock(&my_mutex);//释放互斥量
//打印错误日志
return ;
}
}
//进行其它操作
pthread_mutex_unlock(&my_mutex);
4、条件变量的销毁
函数:
int pthread_cond_destroy(pthread_cond_t *cond)
参数:指向要销毁的条件变量的指针
返回值:成功返回0,失败返回错误码(包括:EBUSY)
销毁指定的条件变量,销毁后将变得不可用。若需要重新使用,可以使用函数pthread_cond_init()重新初始化。
确保没有任何线程拥有该条件变量时再将其销毁。
北岛夜话



发表评论