北岛夜话
原创工业智能控制领域(PLC、单片机/嵌入式、机器人、通信、机器视觉)的技术及经验分享。
文章572 浏览9931491

线程同步(2)——条件变量

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

cover.png

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()重新初始化。

确保没有任何线程拥有该条件变量时再将其销毁。

取消

感谢您的支持,欢迎常来看看!

扫码支持
一点动力,多少随意

打开支付宝扫一扫,即可进行扫码打赏哦

最后编辑于:2026/08/15作者: 北岛李工

发表评论

恭祝各位朋友在丙午马年新春快乐,工作顺利,阖家安康!

×